当前位置: 首页 > 后端技术 > Node.js

JS函数式编程——函数组合与柯里化

时间:2023-04-03 23:08:16 Node.js

我们都知道SingleResponsibilityPrinciple,其实就是面向对象SOLID中的S(SRP,Singleresponsibilityprinciple)。在函数公式中,每个函数都是一个单元,它应该只做一件事。但现实世界总是复杂的,将现实世界映射到编程时,单一的功能意义不大。这时候就需要函数组合和柯里化了。链式调用如果你用过jQuery,你就知道什么是链式调用,比如$('.post').eq(1).attr('data-test','test').javascriptnativestrings和的方法array也可以写链式调用的样式:'Hello,world!'.split('').reverse().join('')//"!dlrow,olleH"第一次链式调用是基于对象的,如果上面的方法split,reverse,join都是从前面的对象“Hello,world!”中分离出来的,是行不通的。在函数式编程中,方法是独立于数据的,我们可以用函数式的方式改写上面的内容:constsplit=(tag,xs)=>xs.split(tag)constreverse=xs=>xs。reverse()constjoin=(tag,xs)=>xs.join(tag)join('',reverse(split('','Hello,world!')))//"!dlrow,olleH"你确定说,你在开玩笑。这比链接调用好在哪里?这仍然取决于数据。如果“你好,世界!”不通过,你的一系列功能组合就不行了。这里唯一的优点是可以重用那些单独的方法。别慌,以后还有那么多内容,我无论如何都会给你优化(傻瓜)的。在改造之前,我们先介绍两个概念,partialapplication和currying。PartialapplicationPartialapplication是一个处理函数参数的过程,他会接收一些参数,然后返回一个接受较少参数的函数。这是一个部分应用程序。我们使用bind来实现:constaddThreeArg=(x,y,z)=>x+y+z;constaddTwoArg=addThreeNumber.bind(null,1)constaddOneArg=addThreeNumber.bind(null,1,2)addTwoArg(2,3)//6addOneArg(7)//10上面使用bind生成另外两个函数,这分别接受其余参数。这是一个部分应用程序。当然你也可以通过其他方式来实现。部分应用程序的问题部分应用程序的主要问题是无法直接推断出它返回的函数的类型。如前所述,部分应用返回一个接受较少参数的函数,而不指定返回多少个参数。这是隐含的东西,你需要看代码。这样你才知道返回的函数接收了多少个参数。CurryingCurrying的定义:您可以调用一个函数而无需一次将所有参数传递给它。该函数将返回一个函数来接收下一个参数。constadd=x=>y=>x+yconstplusOne=add(1)plusOne(10)//11柯里化函数返回一个只接受一个参数的函数,返回的函数类型可以预测。当然,在实际开发中,还有很多函数是没有柯里化的。我们可以使用一些实用函数来转换它们:constcurry=(fn)=>{//fn可以是任何参数的函数constarity=fn.length;返回函数$curry(...args){if(args.lengthxs.split(tag))constsplit=(tag,xs)=>xs.split(tag)//我现在需要一个函数来拆分","constsplitComma=currySplit(',')//bycurryconstsplitComma=string=>split(',',string)可以看到柯里化函数在生成新函数时,与数据无关。比较生成新函数的两个过程,没有柯里化的那个更冗长一点。函数组合先给出代码:constcompose=(...fns)=>(...args)=>fns.reduceRight((res,fn)=>[fn.call(null,...res)],参数)[0];实际上,compose做了两件事:接收一组函数,返回一个函数,不立即执行函数组合函数,将传递给它的函数从左到右组合起来。上面的reduce可能有的同学不是很熟悉吧,我举个2元3元的例子:constcompose=(f,g)=>(...args)=>f(g(...args))constcompose3=(f,g,z)=>(...args)=>f(g(z(...args)))函数调用是从左到右,数据流向是从左到右相同。当然,你可以定义从右到左,但它在语义上没有那么表达。好的,现在让我们优化我们原来的例子:>xs.join(tag))constreverseWords=compose(join(''),reverse,split(''))reverseWords('你好,世界!');是不是更简洁易懂。这里的reverseWords也是我们之前讲的Pointfree的代码风格。它不依赖于数据和外部状态,它是一个组合的函数。Pointfree我在上一篇文章中介绍了JS函数式编程——概念,也解释了它的优缺点。感兴趣的朋友可以看看。函数组合结合律我们先来复习一下小学知识加法的结合律:a+(b+c)=(a+b)+c。我就不解释了,你应该能看懂。回过头来看,函数组合其实有一个结合律:compose(f,compose(g,h))===compose(compose(f,g),h);这对我们编程是一个好处,我们的函数组合可以随意组合和缓存:constsplit=curry((tag,xs)=>xs.split(tag))constreverse=xs=>xs.reverse()constjoin=curry((tag,xs)=>xs.join(tag))constgetReverseArray=compose(reverse,split(''))constreverseWords=compose(join(''),getReverseArray)reverseWords('你好,世界!');脑图补充:OK,接下来介绍类别轮,和函子。