在7分钟内深刻理解咖喱
Eric Elliott’s exceptional Composing Software series is initially what got me excited about functional programming. It's a must-read.
埃里克·埃利奥特(Eric Elliott)杰出的合成软件系列最初使我对函数式编程感到兴奋。 这是必读的。
At one point in the series, he mentioned currying. Both computer science and mathematics agree on the definition:
在系列中的某一点上,他提到了柯林 。 计算机科学和数学都同意以下定义:
Currying turns multi-argument functions into unary (single argument) functions.
Currying将多参数函数转换为一元(单参数)函数。
Curried functions take many arguments one at a time. So if you have
令行禁止功能需要很多参数一次一个 。 所以如果你有
greet = (greeting, first, last) => `${greeting}, ${first} ${last}`;greet('Hello', 'Bruce', 'Wayne'); // Hello, Bruce Wayne
Properly currying greet
gives you
正确greet
会给你
curriedGreet = curry(greet);curriedGreet('Hello')('Bruce')('Wayne'); // Hello, Bruce Wayne
This 3-argument function has been turned into three unary functions. As you supply one parameter, a new function pops out expecting the next one.
该3参数函数已变成三个一元函数。 当您提供一个参数时,会弹出一个新函数,等待下一个。
合适吗? (Properly?)
I say "properly currying" because some curry
functions are more flexible in their usage. Currying's great in theory, but invoking a function for each argument gets tiring in JavaScript.
我说“适当地咖喱”是因为某些curry
函数的用法更加灵活。 Currying理论上很棒,但是为每个参数调用一个函数在JavaScript中很累人。
Ramda's curry
function lets you invoke curriedGreet
like this:
curriedGreet
的 curry
函数可让您像下面这样调用curriedGreet
:
// greet requires 3 params: (greeting, first, last)// these all return a function looking for (first, last)
curriedGreet('Hello');
curriedGreet('Hello')();
curriedGreet()('Hello')()();// these all return a function looking for (last)
curriedGreet('Hello')('Bruce');
curriedGreet('Hello', 'Bruce');
curriedGreet('Hello')()('Bruce')();// these return a greeting, since all 3 params were honored
curriedGreet('Hello')('Bruce')('Wayne');
curriedGreet('Hello', 'Bruce', 'Wayne');
curriedGreet('Hello', 'Bruce')()()('Wayne');
Notice you can choose to give multiple arguments in a single shot. This implementation's more useful while writing code.
请注意,您可以选择在一个镜头中给出多个参数。 此实现在编写代码时更有用。
And as demonstrated above, you can invoke this function forever without parameters and it’ll always return a function that expects the remaining parameters.
如上所示,您可以永久不带参数地调用此函数,并且它将始终返回需要剩余参数的函数。
这怎么可能? (How's This Possible?)
Mr. Elliot shared a curry
implementation much like Ramda's. Here’s the code, or as he aptly called it, a magic spell:
Elliot先生与Ramda一样,分享了curry
实现方式。 这是代码,或者他恰当地称呼它为魔术:
const curry = (f, arr = []) => (...args) =>((a) => (a.length === f.length ? f(...a) : curry(f, a)))([...arr, ...args]);
嗯...? (Umm… ?)

Yeah, I know... It's incredibly concise, so let's refactor and appreciate it together.
是的,我知道...简明扼要,所以让我们一起重构和欣赏它。
此版本工作相同 (This Version Works the Same)
I've also sprinkled debugger
statements to examine it in Chrome Developer Tools.
我还添加了debugger
语句以在Chrome开发人员工具中对其进行检查。
curry = (originalFunction, initialParams = []) => {debugger;return (...nextParams) => {debugger;const curriedFunction = (params) => {debugger;if (params.length === originalFunction.length) {return originalFunction(...params);}return curry(originalFunction, params);};return curriedFunction([...initialParams, ...nextParams]);};
};
Open your Developer Tools and follow along!
打开您的开发人员工具,然后继续!
我们开工吧! (Let's Do This!)
Paste greet
and curry
into your console. Then enter curriedGreet = curry(greet)
and begin the madness.
将greet
和curry
粘贴到您的控制台中。 然后输入curriedGreet = curry(greet)
并开始疯狂。
暂停第2行 (Pause on line 2)

Inspecting our two params we see originalFunction
is greet
and initialParams
defaulted to an empty array because we didn’t supply it. Move to the next breakpoint and, oh wait… that’s it.
检查我们的两个参数,我们看到originalFunction
被greet
并且initialParams
默认为空数组,因为我们没有提供它。 移至下一个断点,哦,等等……就是这样。
Yep! curry(greet)
just returns a new function that expects 3 more parameters. Type curriedGreet
in the console to see what I’m talking about.
是的 curry(greet)
只是返回一个新函数,该函数需要另外3个参数。 在控制台中键入curriedGreet
以查看我在说什么。
When you’re done playing with that, let’s get a bit crazier and do sayHello = curriedGreet('Hello')
.
当您完成此操作后,让我们有点疯狂,然后说sayHello = curriedGreet('Hello')
。
暂停第4行 (Pause on line 4)

Before moving on, type originalFunction
and initialParams
in your console. Notice we can still access those 2 parameters even though we’re in a completely new function? That’s because functions returned from parent functions enjoy their parent’s scope.
在继续之前,请在控制台中键入originalFunction
和initialParams
。 注意,即使我们处于一个全新的功能中,我们仍然可以访问这两个参数? 这是因为从父函数返回的函数享有父函数的作用域。
现实生活中的继承 (Real-life inheritance)
After a parent function passes on, they leave their parameters for their kids to use. Kind of like inheritance in the real life sense.
父函数传递后,他们将其参数留给孩子使用。 在现实生活中有点像继承。
curry
was initially given originalFunction
and initialParams
and then returned a “child” function. Those 2 variables weren’t disposed of yet because maybe that child needs them. If he doesn’t, then that scope gets cleaned up because when no one references you, that’s when you truly die.
最初为curry
提供了originalFunction
和initialParams
,然后返回了一个“子”函数。 这两个变量尚未处理,因为也许那个孩子需要它们。 如果他不这样做, 那么该范围将被清理,因为当没有人提及您时,那才是您真正死亡的时间。
好,回到第4行… (Ok, back to line 4…)
Inspect nextParams
and see that it’s ['Hello']
…an array? But I thought we said curriedGreet(‘Hello’)
, not curriedGreet(['Hello'])
!
检查nextParams
,看看它是['Hello']
…数组吗? 但是我以为我们说的是curriedGreet('Hello')
,而不是curriedGreet(['Hello'])
!
Correct: we invoked curriedGreet
with 'Hello'
, but thanks to the rest syntax, we’ve turned 'Hello'
into ['Hello']
.
正确:我们用'Hello'
调用了curriedGreet
,但是由于其余的语法 ,我们已经将 'Hello'
变成了['Hello']
。
是吗? (Y THO?!)
curry
is a general function that can be supplied 1, 10, or 10,000,000 parameters, so it needs a way to reference all of them. Using the rest syntax like that captures every single parameter in one array, making curry
's job much easier.
curry
是一个通用函数,可以提供1、10或10,000,000个参数,因此它需要一种引用所有参数的方法。 使用剩下的语法可以捕获一个数组中的每个参数,从而curry
的工作。
Let’s jump to the next debugger
statement.
让我们跳到下一个debugger
语句。
现在是第6行,但请稍等。 (Line 6 now, but hold on.)
You may have noticed that line 12 actually ran before the debugger
statement on line 6. If not, look closer. Our program defines a function called curriedFunction
on line 5, uses it on line 12, and then we hit that debugger
statement on line 6. And what’s curriedFunction
invoked with?
您可能已经注意到第12行实际上是在第6行的debugger
语句之前运行的。如果没有,请仔细观察。 我们的程序在第5行定义了一个称为curriedFunction
的函数,在第12行使用了该函数, 然后在第6行命中了该debugger
语句curriedFunction
调用了什么?
[...initialParams, ...nextParams];
Yuuuup. Look at params
on line 5 and you’ll see ['Hello']
. Both initialParams
and nextParams
were arrays, so we flattened and combined them into a single array using the handy spread operator.
悠悠 查看第5行的params
,您会看到['Hello']
。 initialParams
和nextParams
都是数组,因此我们使用方便的散布运算符展平并将它们组合为单个数组。
Here’s where the good stuff happens.
这是好事发生的地方。

Line 7 says “If params
and originalFunction
are the same length, call greet
with our params and we’re done.” Which reminds me…
第7行“如果params
和originalFunction
是相同的长度,呼叫greet
我们的PARAMS,我们就大功告成了。” 这使我想起…
JavaScript函数也有长度 (JavaScript functions have lengths too)
This is how curry
does its magic! This is how it decides whether or not to ask for more parameters.
这就是curry
的神奇功效! 这就是它决定是否要求更多参数的方式。
In JavaScript, a function’s .length
property tells you how many arguments it expects.
在JavaScript中,函数的.length
属性告诉您期望的参数数量 。
greet.length; // 3iTakeOneParam = (a) => {};
iTakeTwoParams = (a, b) => {};iTakeOneParam.length; // 1
iTakeTwoParams.length; // 2
If our provided and expected parameters match, we’re good, just hand them off to the original function and finish the job!
如果我们提供的参数与预期的参数匹配,那么很好,只需将其交给原始功能即可完成工作!
那是芭蕾舞演员? (That’s baller ?)
But in our case, the parameters and function length are not the same. We only provided ‘Hello’
, so params.length
is 1, and originalFunction.length
is 3 because greet
expects 3 parameters: greeting, first, last
.
但是在我们的例子中,参数和函数长度是不一样的。 我们只提供了'Hello'
,所以params.length
是1,而originalFunction.length
是3,因为greet
需要3个参数: greeting, first, last
。
那么接下来会发生什么呢? (So what happens next?)
Well since that if
statement evaluates to false
, the code will skip to line 10 and re-invoke our master curry
function. It re-receives greet
and this time, 'Hello'
, and begins the madness all over again.
好吧,因为if
语句的计算结果为false
,所以代码将跳至第10行并重新调用我们的master curry
函数。 它再次收到greet
,这一次是'Hello'
,然后再次开始疯狂。
That’s recursion, my friends.
那是递归 ,我的朋友们。
curry
is essentially an infinite loop of self-calling, parameter-hungry functions that won’t rest until their guest is full. Hospitality at its finest.
curry
本质上是一个无限的自我调用,需要参数的函数的循环,直到他们的客人吃饱为止。 最好的款待。

回到第2行 (Back at line 2)
Same parameters as before, except initialParams
is ['Hello']
this time. Skip again to exit the cycle. Type our new variable into the console, sayHello
. It’s another function, still expecting more parameters, but we’re getting warmer…
与之前相同的参数,除了这次的initialParams
是['Hello']
。 再次跳过以退出循环。 在控制台中输入我们的新变量sayHello
。 这是另一个功能,仍然需要更多参数,但是我们越来越热……
Let’s turn up the heat with sayHelloToJohn = sayHello('John')
.
让我们用sayHelloToJohn = sayHello('John')
。
We’re inside line 4 again, and nextParams
is ['John']
. Jump to the next debugger on line 6 and inspect params
: it’s ['Hello', 'John']
! ?
我们再次进入第4行,并且nextParams
是['John']
。 跳至第6行的下一个调试器并检查params
:它是['Hello', 'John']
! ?

为什么,为什么,为什么? (Why, why, why?)
Because remember, line 12 says “Hey curriedFunction
, he gave me 'Hello'
last time and ‘John’
this time. Take them both in this array [...initialParams, ...nextParams]
.”
因为请记住,第12行说:“嘿curriedFunction
,他上次给了我'Hello'
, curriedFunction
给了我'John'
。 将它们都放入此数组[...initialParams, ...nextParams]
。”

Now curriedFunction
again compares the length
of these params
to originalFunction
, and since 2 < 3
we move to line 10 and call curry
once again! And of course, we pass along greet
and our 2 params, ['Hello', 'John']
现在curriedFunction
又比较length
,这些的params
到originalFunction
,自2 < 3
我们搬到10号线和呼叫curry
再次! 当然,我们会同时greet
和我们的两个参数, ['Hello', 'John']

We’re so close, let’s finish this and get the full greeting back!
我们是如此接近,让我们完成此步骤并获得完整的问候!
sayHelloToJohnDoe = sayHelloToJohn('Doe')
sayHelloToJohnDoe = sayHelloToJohn('Doe')
I think we know what happens next.
我想我们知道接下来会发生什么。



行动完成 (The Deed Is Done)
greet
got his parameters, curry
stopped looping, and we’ve received our greeting: Hello, John Doe
.
greet
了他的参数, curry
停止了循环,我们已经收到了问候: Hello, John Doe
。
Play around with this function some more. Try supplying multiple or no parameters in one shot, get as crazy as you want. See how many times curry
has to recurse before returning your expected output.
进一步使用此功能。 尝试一次提供多个参数或不提供任何参数,并根据需要疯狂设置。 在返回您的预期输出之前,先查看curry
必须重复多少次。
curriedGreet('Hello', 'John', 'Doe');
curriedGreet('Hello', 'John')('Doe');
curriedGreet()()('Hello')()('John')()()()()('Doe');
Many thanks to Eric Elliott for introducing this to me, and even more thanks to you for appreciating curry
with me. Until next time!
非常感谢埃里克·埃利奥特 ( Eric Elliott)向我介绍了这一点,也非常感谢您与我一起欣赏curry
。 直到下一次!
For more content like this, check out yazeedb.com!
有关更多内容,请访问yazeedb.com !
翻译自: https://www.freecodecamp.org/news/deeply-understand-currying-in-7-minutes/
相关文章:

github后端开发面试题大集合(一)
作者:小海胆链接:https://www.nowcoder.com/discuss/3614?type0&order0&pos5&page0?fromwb来源:牛客网 转载自github,中文--->链接在这,英文---->链接在这文章较长,我把它拆分了三个部…

微信小程序左滑删除效果的实现完整源码附效果图
效果图: 功能描述,小程序列表左滑删除功能的实现完整源代码实现: <view wx:for{{friends}} wx:key"" wx:if{{groupType4}} catchtap"nav_oneChat" id"{{item._id}}" class"item p_r" style"…

Eclipse下修改工程名
一。 右键工程:Refactor->Rename,或选中工程按F2,修改名称 二。右键工程:Properties->Web Project Settings,修改Context Root三。1.找到项目所在位置(如图): 2.修改项目目录/.setting目录…

git操作手册_基本的Git手册
git操作手册介绍 (Introduction) Hi! I am Sanjula, and in this guide I hope to teach you a little bit about Git including:嗨! 我是Sanjula ,在本指南中,我希望教您一些有关Git的知识,包括: What Git is 什么是…

PHP 接入(第三方登录)QQ 登录 OAuth2.0 过程中遇到的坑
前言 绝大多数网站都集成了第三方登录,降低了注册门槛,增强了用户体验。最近看了看 QQ 互联上 QQ 登录的接口文档。接入 QQ 登录的一般流程是这样的:先申请开发者 -> 然后创建应用(拿到一组 AppId 和 AppKey)-> …

npm全局环境变量配置,全局配置cnpm
今天新电脑想安装node.js , 发现最新版本的node.js已经不支持win7了,但是又不想换系统,所以找了个旧版本,这里不多说了。如果找不到旧版本的node下载,可以去我的QQ交流群文件里面下载,QQ群号:17…

NTFS for Mac OS X:使用Brew安裝NTFS-3G
在最新的OSX系統中,其實已經完整兼容NTFS文件系統,只是出於安全考慮默認是以只讀模式掛載NTFS分區的,可以透過diskutil查詢硬碟UUID,重新以讀寫(rw)權限掛載,具體的可以參照這裡 當然,也有現成的軟體幫助你…

javascript开关_JavaScript开关案例简介
javascript开关In this short article, I will introduce you to JavaScript switch cases and how to use them with practical examples.在这篇简短的文章中,我将向您介绍JavaScript切换案例以及如何通过实际示例使用它们。 This article will explain better wi…

C++中一个class类对象占用多少内字节(7个例子,很清楚)
一个空的class在内存中多少字节?如果加入一个成员函数后是多大?这个成员函数存储在内存中什么部分? 一个Class对象需要占用多大的内存空间。最权威的结论是: *非静态成员变量总合。 *加上编译器为了CPU计算,作出的数据…

Java学习----到底调用哪一个方法(多态)
public class Father {public void print() {System.out.println("Father:print()");} } public class Son extends Father{// 方法的覆盖:子类重写父类的同名方法 Overridepublic void print() {System.out.println("Son:print()");}// Father…

mpvue 转uni-app 操作记录
1.创建一个uni-app 的项目,把原有的mpvue项目手动迁移过来 2.用到mpvue特性的代码,需要修改成uni-app 兼容的,例如 mpvue-wxparse 可以修改成 3.src/app.json 改成 pages.json ,路径格式需要改,如下格式 {"pages": [&…

桌面应用程序 azure_Azure Logic应用程序用例–黑色星期五
桌面应用程序 azureThis blog gives an overview of how Azure Serverless technologies came to rescue when the custom-built integration system went down. Also, it shows the high-level architecture solution built using Azure Serverless services like Logic Apps,…

PHP的静态变量介绍
静态变量只存在于函数作用域内,也就是说,静态变量只存活在栈中。一般的函数内变量在函数结束后会释放,比如局部变量,但是静态变量却不会。就是说,下次再调用这个函数的时候,该变量的值会保留下来。 只要在变…

MySQL 解压版创建用户密码
root 权限进入MySQL: mysql –uroot 查看当前MySQL用户: select user,host from mysql.user; 此处以User为root,Host为localhost的账户为例,将密码改为password的命令: SET PASSWORD FOR rootlocalhost PASSWORD(newp…

git 忽略指定文件夹的上传
我们在使用 git 开发的时候,有些插件的模块文件通过npm install 就可以下载,一般是不上传到 git 中的(因为文件太多会导致很耗时),例如 我的 node_modules 文件夹,不想上传,我们应该这么做。 我们需要创建一…

数据库初学者_面向初学者的免费6小时数据科学课程
数据库初学者Data science is considered the "sexiest job of the 21st century." Learn data science in this full 6-hour course for absolute beginners from Barton Poulson of datalab.cc.数据科学被认为是“ 21世纪最艰巨的工作”。 通过datalab.cc的 Barton…

网页抓取及下载
downAndroidApk.php <?php /* 命令行 d: cd ApacheServer\php php.exe D:\ApacheServer\web\crawl\downAndroidApk.php --appidFileD:\ApacheServer\web\crawl\youxi.txt --newDirD:\ApacheServer\web\crawl\requestNewDir*/ // 判断必须在php-cli模式下运行,即…

javascript中关于this指向问题详解
前 言 LiuDaP 在前端的学习中,我们必然要用到js,js可以说是前端必不可少的的东西。在学习js的过程中,我们会经常用到this这个东西,而this的指向问题就变得尤为重要。今天正好有空闲时间,就给大家详细介绍一下js中关于…

mpvue 转uniapp 导航栏样式错乱问题修复 tabbar 样式修复
效果图:修改前,修改后 找了半天没找到原因,只能自己改样式了,下面是样式代码(在app.vue 里面加上就行) <style>/*每个页面公共css */uni-tabbar {box-sizing: border-box;position: fixed;left: 0;bo…

css规则_CSS规则,将使您的生活更轻松
css规则by Nick Gard尼克加德(Nick Gard) CSS规则,将使您的生活更轻松 (CSS rules that will make your life easier) After years of writing and maintaining a couple of very large web projects and numerous smaller ones, I have developed some heuristics…

在mybatis中模糊查询有三种写法
<select id"selectStudentsByName" resultType"Student"> <!--第一种--> <!-- select id,name,age,score from student where name like % #{0} % --> <!--第二种--> <!-- select id,name,age,score from student wher…

BZOJ 3566: [SHOI2014]概率充电器
题目:http://www.lydsy.com/JudgeOnline/problem.php?id3566 首先这题正着想不好想,考虑补集转化。 先dfs一遍,令f[u](1-p[u])*∏(1-(1-f[v])*w) f[u]表示u这个点通过其子树并不能联通的概率。 然后考虑v从其父亲连过来的情况,设…

小程序云开发,订阅消息定时批量发送实现代码
需求:做一个类似抽奖结果通知的订阅消息提醒 实现流程: 每个用户需要先授权订阅消息接收,授权成功后把数据存到云开发的数据集合里面,再写个定时器,遍历数据集合的所有数据,拿到后遍历发送订阅消息&#…

机器学习速成课程
Learn the basics of machine learning and data science in this crash course tutorial for beginners from AI Sciences Academy. This course will give you the foundation you need to start learning more advanced material.在此速成课程教程中为AI Sciences Academy的…

H5 画布解决跨域问题,画布保存为图片显示在页面上
实现功能:uniapp H5 使用画布,绘画完之后保存为图片全屏显示完整实现代码,跨域解决方案。 跨域图片解决方案一:(使用base64编码)网络图片放到画布里面绘画 跨域图片解决方案二:(使…

1、IO输入输出流 简介
IO流的分类: * 流向: * 输入流 读取数据 * 输出流 写出数据 * 数据类型: * 字节流 * 字节输入流 读取数据 InputStream * 字节输出流 写出数据 OutputStream * 字符流 * 字符输入流 读取数据 Reader * 字符输出流 写出数据 Writer * * 注意&…

mern技术栈好处?_通过构建运动追踪器应用程序来学习MERN堆栈(MERN教程)
mern技术栈好处?The MERN stack is a popular stack of technologies for building a modern single-page application. In this video course I developed, you will learn the MERN stack by building a full stack exercise tracker application.MERN堆栈是用于构建现代单页…

使用html5进行视频播放
一直以来网页大多是使用 flash 来播放视频。在目前唱衰 flash 的环境下,HTML5 为我们带来了一个网页内视频播放的解决方案—— <video>标签。 在HTML5 中,可以通过HTML标签“audio”和“video”来支持嵌入式的媒体,使开发者能够方便地将…

Linux学习之系统时间同步
一、系统时间的设置 在Linux中设置系统时间,可以用date命令: 1 //查看时间 2 [rootlocalhost ~]# date 3 2008年 12月 12日 星期五 14:44:12 CST 4 //修改时间 5 [rootlocalhost ~]# date --set "1/1/09 00:01" < (月/日/年时:分…

uniapp(一) 项目架构,封装
前言: 最近需要搭建一套基于uniapp 的代码模板,适应各平台的快速打包部署,为提高代码复用率,提升生产力,所以需要构建一套优雅的前端项目架构,下面分享记录一下我的封装。 代码封装我暂时分为三个层面&…