定义


curry**
translating
the evaluation of a function that takes multiple args(多参函数)
into
evaluation a sequence of functions,each with a single arg(一系列单参函数).

即:f(x, y, z) => f(x)(y)(z)

被 curry 化的函数,一般地,参数凑齐了,就会执行,如 loadsh 就是这样处理的。但并没有规范的做法,所以也可以是 f(x, y, z) => f(x, y)(z) 或者 f(x)(y)(z)() 或者 f(x)(y)(z).run() 等等。

It provides a way of automatically managing how arguments are passed to functions and expression.

一些语言中,几乎总是使用 curry 实现多参函数,如 ML/Haskell。在这些语言中,函数只能有一个参数(规范做法)。

currying 与 partial application 有点类似,但不一样。

只接收一个参数的成为一元函数,接收两个参数称为两元参数,接收多个参数的称为多元函数。接收不确定参数的称为变参函数。柯里化是把一个多参数函数转换成一个嵌套的一元函数的过程。

通用 curry 函数

参数够了就执行:

  1. const curry = (fn) => {
  2. return function curryFunc(...arg) {
  3. if (arg.length < fn.length) {
  4. return function () {
  5. return curryFunc.apply(null, [...arg, ...arguments]);
  6. };
  7. }
  8. return fn.apply(null, arg);
  9. }
  10. };
  11. const func = (a, b) => console.log(a - b);
  12. curry(func)(1)(2)

专用 curry 函数

对使用场景有要求,不具备通用性:

  1. function add(x, y){ return x + y }
  2. function sub(x, y){ return x - y; }
  3. function mul(x, y){ return x * y;}
  4. function curry(f, x){
  5. return function(y){
  6. return f(x, y);
  7. }
  8. }
  9. curry(add, 3)(4); // 7
  10. curry(sub, 8)(6); // 2
  11. curry(mul, 5)(6); // 30

参数为0个时触发调用

  1. const coreAdd = (...rest) => {
  2. const args = rest
  3. return args.reduce((acc, element) => ( acc += element ), 0)
  4. }
  5. function addCurry() {
  6. const args = Array.prototype.slice.call(arguments)
  7. return function() {
  8. const newArgs = Array.prototype.slice.call(arguments)
  9. const totalArgs = newArgs.concat(args)
  10. if(newArgs.length === 0) return coreAdd(...totalArgs)
  11. return addCurry(...totalArgs)
  12. }
  13. }
  14. const addValue = addCurry(2, 3)(1)(8, 5)()
  15. alert(addValue) // 19

https://medium.com/@anilchaudhary453/method-chaining-currying-javascript-b6fc3324592c

触发条件是显式 .result()

  1. function add (num) {
  2. function adder (n) {
  3. if(n !== undefined) {
  4. num += n
  5. return adder
  6. } else {
  7. return num
  8. }
  9. }
  10. adder.result = function() {
  11. return num
  12. }
  13. return adder
  14. }
  15. add(1)(2)(3)(4)(5).result() // 15
  16. add(1)(2)(3).result() // 6
  17. add(1).result() // 1

lodash 中的 curry

lodash 中的 curry 支持 placeholder,以便于调整参数的提供次序。

  1. var abc = function(a, b, c) {
  2. return [a, b, c];
  3. };
  4. var curried = _.curry(abc);
  5. curried(1)(2)(3);
  6. // => [1, 2, 3]
  7. curried(1, 2)(3);
  8. // => [1, 2, 3]
  9. curried(1, 2, 3);
  10. // => [1, 2, 3]
  11. // Curried with placeholders.
  12. curried(1)(_, 3)(2);
  13. // => [1, 2, 3]

参考:https://lodash.com/docs/#curry

偏应用

允许开发者部分地应用函数,即将给定函数的部分参数固化,然后返回一个函数,该函数仅接收未固化的参数。

通用 partial 函数

  1. Function.prototype.partial = function(...args){
  2. for (let i = args.length; i < this.length; i++){
  3. args.push(undefined) // 补齐,跟fn的参数列表对应上
  4. }
  5. return (...remainArgs) => {
  6. let realArgs = []
  7. let j = 0;
  8. args.forEach((arg, i) => {
  9. if(arg === undefined){
  10. realArgs.push(remainArgs[j++])
  11. } else {
  12. realArgs.push(arg)
  13. }
  14. })
  15. return this(...realArgs)
  16. }
  17. }

使用示例 1

  1. let timer3s = setTimeout.partial(undefined, 3000)
  2. timer3s(() => console.log('hello')) // 3s后输出hello
  3. timer3s(() => console.log('hello2')) // 3s后输出hello2
  4. timer3s(() => console.log('hello3')) // 3s后输出hello3

使用示例 2

  1. function add4(a,b,c,d) { return a + b + c + d }
  2. var _add4 = add4.partial(1,undefined,3,undefined);
  3. _add4(2,4) // 10
  4. _add4(3,5) // 12

参考:

https://simple.wikipedia.org/wiki/Currying
https://en.wikipedia.org/wiki/Currying
https://javascript.info/currying-partials
https://blog.benestudio.co/currying-in-javascript-es6-540d2ad09400
https://blog.bitsrc.io/understanding-currying-in-javascript-ceb2188c339
https://codeburst.io/currying-in-javascript-ba51eb9778dc
https://github.com/lodash/lodash/blob/npm/_createCurry.js