在7分钟内深刻理解咖喱

news/2024/7/7 21:26:07

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参数函数已变成三个一元函数。 当您提供一个参数时,会弹出一个新函数,等待下一个。

dog-properly-currying-a-function-1

合适吗? (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:

curriedGreetcurry函数可让您像下面这样调用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… ?)

that-code-is-hard-to-read-cmm

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.

greetcurry粘贴到您的控制台中。 然后输入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.

检查我们的两个参数,我们看到originalFunctiongreet并且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.

在继续之前,请在控制台中键入originalFunctioninitialParams 。 注意,即使我们处于一个全新的功能中,我们仍然可以访问这两个参数? 这是因为从父函数返回的函数享有父函数的作用域。

现实生活中的继承 (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提供了originalFunctioninitialParams ,然后返回了一个“子”函数。 这两个变量尚未处理,因为也许那个孩子需要它们。 如果他不这样做, 那么该范围将被清理,因为当没有人提及您时,那才是您真正死亡的时间。

好,回到第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']initialParamsnextParams都是数组,因此我们使用方便的散布运算符展平并将它们组合为单个数组。

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行“如果paramsoriginalFunction是相同的长度,呼叫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 ,这些的paramsoriginalFunction ,自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/


http://lihuaxi.xjx100.cn/news/238057.html

相关文章

介绍Zabbix的两种监控模式(主动模式和被动模式)

Zabbix agent检测分为两种模式&#xff1a;主动模式和被动模式 被动模式&#xff0c;也是默认的Zabbix监控模式&#xff0c;被动模式是相对于proxy来说的。proxy主动发送数据就是主动模式&#xff0c;proxy等待server的请求再发送数据就是被动模式。主动模式有个好处就是可以有…

mysql存储过程

基础查询 首先创建一张students学生表&#xff0c;增加字段与插入数据如下 创建不带参数的存储过程 查看学生个数 DROP PROCEDURE IF EXISTS select_students_count; -- 没有括号() DELIMITER ;; CREATE PROCEDURE select_students_count() BEGINSELECT count(id) from stude…

ABP理论学习之数据传输对象(DTO)

本篇目录 为何需要DTO 领域层抽象数据隐藏序列化和懒加载问题DTO惯例和验证 DTO和实体的自动映射 使用特性和扩展方法进行映射帮助接口DTO用于应用层和 展现层间的数据传输。 展现层调用具有DTO参数的应用服务方法&#xff0c;然后应用服务使用领域对象来执行一些特定的业务逻辑…

搭建Zabbix分布式监控

1、实现zabbix监控nginx 实验环境&#xff1a; server1 172.25.1.1 server redhat7 test1 172.25.1.11 agent redhat7 在“手动添加”主机的基础上进行扩展 开启服务&#xff1a; [rootserver ~]# systemctl…

区块链侧链技术优势

想知道更多关于区块链技术知识&#xff0c;请百度【链客区块链技术问答社区】 链客&#xff0c;有问必答&#xff01;asch使用的是不同于以太坊和比特币的侧链架构&#xff0c;dapp是运行在侧链上的&#xff0c;每套侧链对应一个dapp。 侧链的独立性 侧链架构的好处是代码和数据…

如何删除GO语言中安装的包

为什么80%的码农都做不了架构师&#xff1f;>>> 搜索了一下&#xff0c;只发现有安装包的相关说明&#xff0c;go的命令中&#xff0c;没有uninstall之类的命令。 参考&#xff1a; Go packages 中的解释&#xff1a; To uninstall, just delete the folder with t…

密码破解

思科启动过程&#xff1a;1、POTS&#xff1a;加点自检 - 设备通电以后&#xff0c;检查设备的各种硬件是否完好&#xff08;橙--绿&#xff0c;无故障的是绿色或者灭掉&#xff09; 2、查找操作系统(yes/no)flash网络3、查找“启动配置文件”&#xff1a;startup-config(yes/n…

git操作手册_基本的Git手册

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