javascript的currying函数
curring的概念将函数式编程的概念和默认参数以及可变参数结合在一起.一个带n个参数,curried的函数固化第一个参数为固定参数,并返回另一个带n-1个参数的函数对象,分别类似于LISP的原始函数car和cdr的行为。currying能泛化为偏函数应用(partial function application, PFA),p 这种函数将任意数量(顺序)的参数的函数转化为另一个带剩余参数的函数对象。
最早期的curry函数有点多态的意味,就是根据函数参数在内部选用分支:
//http://www.openlaszlo.org/pipermail/laszlo-user/2005-March/000350.html // ★★On 8 Mar 2005, at 00:06, Steve Albin wrote: function add(a, b) { if (arguments.length < 1) { return add; } else if (arguments.length < 2) { return function (c) { return a + c } } else { return a + b; } } var myadd = add( 2 ); var total = myadd(3); |
日本的一个先行者可能在未搞清arguments也能用Array的原生方法转换为数组的时候,用非常复杂的正则与eval搞出一个更接近现代currying意味的函数。
function curry(fun) { if ( typeof fun != 'function' ) { throw new Error( "The argument must be a function." ); } if (fun.arity == 0) { throw new Error( "The function must have more than one argument." ); } var funText = fun.toString(); var args = / function .*\((.*)\)(.*)/.exec(funText)[1].split( ', ' ); var firstArg = args.shift(); var restArgs = args.join( ', ' ); var body = funText.replace(/ function .*\(.*\) /, "" ); var curriedText = "function (" + firstArg + ") {" + "return function (" + restArgs + ")" + body + "}" ; eval( "var curried =" + curriedText); return curried; } |
接着是闭包的流行,与数组转换arguments的技术的发现,现代currying函数终于粉墨登场,就好像15~17世纪大航海时代的地理大发现,javascript的世界突然间开阔了许多。
//一个简单的现代currying函数 function curry (fn, scope) { var scope = scope || window; var args = []; for ( var i=2, len = arguments.length; i < len; ++i) { args.push(arguments[i]); }; return function () { fn.apply(scope, args); }; } |
一般的currying函数只有两重,执行情况如下,第一次执行参数不足返回内部函数,第二次执行才最终完成。不过针对这参数,我们还是可以做一些文章。看如下函数:
function sum(){ var result=0; for ( var i=0, n=arguments.length; i<n; i++){ result += arguments[i]; } return result; } alert(sum(1,2,3,4,5)); // 15 |
这就没有所谓的参数不足问题,传入一个参数,它也计算。但不传入参数呢?无错,区别在于有没有参数。我们可以让它不断执行自身,如果参数存在的情况下。最后在没有参数的情况下,一次过执行。换言之,前面的步骤是用于储存参数。
var sum2= curry(sum); sum2= sum2(1)(2)(3)(4)(5); sum2(); // 15 |
比起一般的currying函数,这有点难度。具体看注解:
var curry= function (fn){ //原函数的参数为函数 return function (args){ //内部函数的参数为数组,由于立即执行,因此直接到第三重去 //args是相对于第三重内部函数可是全局变量 var self= arguments.callee; //把自身保存起来(就是那个数组为参数的第二重函数) return function (){ //这才是第二次调用的函数 if (arguments.length){ //如果还有要添加的参数 [].push.apply(args,arguments); //apply把当前传入的所有参数放进args中 return self(args); } else { return fn.apply( this ,args); //apply的第二参数为数组 } } }([]); }; |
或者每次传入多个参数:
但上面的函数有不足之处,最后怎么也要放个括号,我们想只要参数足够就返回结果,多出的参数忽略。改进如下:
function curry(f) { if (f.length == 0) return f; function iterate(args) { if (args.length <= f.length) return f.apply( null , args); return function () { return iterate(args.concat(Array.prototype.slice.call(arguments))); }; } return iterate([]); } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库