函数式编程中的Currying

函数式编程中的Currying

Tags
函数式编程
JavaScript
偏函数
CreatedTime
Aug 22, 2022 07:49 AM
Slug
2021-09-10-curry
UpdatedTime
Last updated August 22, 2022
 
函数柯里化定义:将一个多参数函数拆解成一串连续的链式函数,每个小函数接受单一参数Arity = 1,并return另一个函数接受下一个参数。一种特殊的偏函数。
这种技巧,就叫Currying (柯里化)。
 
柯里化实现:
function curry(fn, argsLength = fn.length) { let args = [] // 保留函数参数 // 返回一个新的函数,这个函数执行时会检查当前参数个数是否达到原函数fn的参数个数 // 如果达到,执行将保留的参数都传给fn,返回执行结果;如果没达到,则返回它自身 return function curried(nextArgs) { args = [...args, nextArgs] if (args.length >= argsLength) { return fn(...args) } else { return curried } } } function sum(x, y, z) { return x + y + z } const currySum = curry(sum) console.log(currySum(10)(10)(10)) // 输出 30
这种实现有问题,就是调用了一次currySum(10)(10)(10)之后,再次调用currySum(10)(10)(10)会报错。因为在第二次再次调用的时候,由于args是「共享」的,args中已经是[10, 10, 10]了。currySum(10)会调用fn返回结果,而不是返回函数自身curried
报错信息是: TypeError: currySum(...) is not a function
解决方法是:
function curry (fn, ARITY = fn.length) { return (function nextCurried (prevArgs) { // 第一次对fn调用curry之后,返回的就是curried1 // 再次调用curried1时,执行nextCurried,返回的是curried2 // 依次类推。返回curried-n函数是个崭新函数,不存在共用的args return function curried (nextArg) { var args = [...prevArgs, nextArg] if (args.length >= ARITY) { return fn(...args) } else { return nextCurried(args) } } })([]) }
对于不明确参数个数的函数,可以通过curry的第二个参数指定参数个数。例如:
function sum(...args) { var sum = 0; for (let i = 0; i < args.length; i++) { sum += args[i]; } return sum; } var curriedSum = curry( sum, 5 ); curriedSum( 1 )( 2 )( 3 )( 4 )( 5 ); // 15
除此之外,还可以使用 lodash.js 的curry函数:
var abc = function(a, b, c) { return [a, b, c]; }; var curried = lodash.curry(abc); curried(1)(2)(3); // => [1, 2, 3] curried(1, 2)(3); // => [1, 2, 3]