当前位置: 首页 > Web前端 > JavaScript

JavaScript函数式编程仿函数

时间:2023-03-27 17:29:06 JavaScript

仿函数(Functor)仿函数是一个特殊的容器,由一个普通对象实现,对象有一个map方法,map方法可以运行一个函数来处理值(变形关系),容器包含值与值的转换关系(这种转换关系就是函数)。函数式编程解决了副作用的存在。函数式编程操作不直接操作值,而是由函子来完成。仿函数是实现地图契约的对象。我们可以把仿函数想象成一个盒子,里面封装了一个Value来处理盒子里的值,我们需要传递一个处理值的函数(纯函数)给盒子的map方法,这个函数会处理价值。最后,map方法返回一个包含新值的框(仿函数)。根据仿函数的定义,我们创建一个仿函数//functorfunctorclassContainer{constructor(value){//仿函数内部保存了这个值。下划线是我不希望外部访问this._value=value}//map方法接收一个处理值的函数map(fn){returnnewContainer(fn(this._value))}}。至此,一个functor已经创建好了,但是this是以面向对象的方式创建的,而不是使用函数式编程写一个functorclassContainer{constructor(value){this._value=value}map(fn){returnContainer.of(fn(this._value))}staticof(value){returnnewContainer(value)}}让x=Container.of(5).map(x=>x+1).map(x=>x-1)但是这个functor还是有一些问题,比如有null值的时候会报错,这会让我们的functor不纯。我们需要拦截空值错误。我们创建一个判断是否为空值的方法。如果是控件,我们直接返回一个空值的Functor,如果有值需要处理,需要使用MayBefunctorletx=Container.of(null).map(x=>x+1).map(x=>x-1)MayBefunctor我们在编程的过程中可能会遇到很多错误,我们需要对这些错误进行相应的处理。MayBefunctor的作用是处理外部空值情况(控制副作用在允许范围内)//MayBefunctorclassMayBe{constructor(value){this._value=value}map(fn){returnthis.isNothing()?MaybeBe.of(null):MayBe.of(fn(this._value))}isNothing(){returnthis._value===未定义||this._value===null}staticof(value){returnnewMayBe(value)}}letx=MayBe.of(null).map(x=>x+1).map(x=>x-1)console.log(x)这个时候我们已经可以正常执行了,但是现在有一个空值的仿函数,但是我们不知道那个地方有一个空值,所以我们创建了两个仿函数,一个用于正常处理,一个用于错误处理。如果正常,则按正常方式创建。处理回调函数,直接返回一个空值的MayBe仿函数,这样报错信息Eitcher仿函数就是用来处理这种情况的。Either函子Eitcher类似于ifelse的处理。两者中的任何一个都不正常。会使函数变得不纯,Eitcher仿函数可以用于异常处理//因为是替代,所以定义两个类Left和Right//记录错误信息的类Left{constructor(value){this._value=value}map(fn){returnthis}staticof(value){returnnewLeft(value)}}//正常处理类Rgiht{constructor(value){this._value=value}map(fn){returnRgiht.of(fn(this._value))}staticof(value){returnnewRgiht(value)}}functionparseJson(str){try{returnRgiht.of(JSON.parse(str))}catch(err错误){returnLeft.of({message:err.message})}}//故意传入错误的数据letr=parseJson('{name:"2"}')r.map(x=>x.name.toUpperCase())console.log(r)IOfunctorIOfunctor中的_value是一个函数,这里把function当作一个值,IOfunctor可以在_value中存放不纯的动作,延迟这个purefuck操作(惰性执行),保证当前操作是纯操作,将不纯操作延迟给调用者处理constfp=require('lodash/fp')//IOfunctorclassIO{constructor(fn){this._value=fn}staticof(value){returnnewIO(function(){returnvalue})}map(fn){//将当前值和传入的fn函数组合成一个新函数returnnewIO(fp.flowRight(fn,this._value))}}letr=IO.of(process).map(x=>x.execPath)console.log(r)console.log(r._value())IO函子包装了一些函数为了我们。当我们传递一个函数时,这个函数可能是一个不纯的操作。不管函数是纯的还是非纯的,IO仿函数在执行过程中返回的结果始终是一个纯操作。我们调用map的时候,总是返回一个functor,但是IOfunctor的_value属性需要合并很多函数在里面,所以里面可能不纯。把这些不纯的操作当延迟调用,也就是我们用IO仿函数来控制副作用发生在一个可控的范围内。liunx下实现cat命令constfp=require('lodash/fp')//IOfunctorclassIO{constructor(fn){this._value=fn}staticof(value){returnnewIO(function(){returnvalue})}map(fn){//将当前值和传入的fn函数组合成一个新的函数returnnewIO(fp.flowRight(fn,this._value))}}letr=IO.of(process.map(x=>x.execPath)functionreadFile(fileName){returnnewIO(()=>fs.readFileSync(fileName,'utf-8'))}functionprint(x){returnnewIO(()=>{console.log(x)returnx})}letcat=fp.flowRight(print,readFile)console.log(cat('package.json')._value()._value())此时IO仿函数出现嵌套问题,导致需要调用嵌套仿函数中的方法。_value()._value()是这样实现的,多层嵌套需要多层调用FolktaleFolktale是一个标准的函数式编程库。与lodash不同的是,它并没有提供很多功能函数,只有一些函数公式处理的操作,比如:compose,curry等,一些仿函数Task,Either,MayBe等,Folktale中curry和compose的简单使用const{compose,curry}=require('folktale/core/lambda')const{toUpper,first}=require('lodash/fp')//与lodash不同,第一个参数表示后续参数个数letf=curry(2,(n1,n2)=>n1+n2)console.log(f(1,2))//compose是函数组合。lodash中的函数组合是flowRightletf2=compose(toUpper,first)console.log(f2(['one','two']))FolktaleFunctors中的任务Functors可以处理异步任务。在异步任务中,会有回调通向地狱之门。使用任务仿函数可以避免回调的嵌套。详情请参考官方文档//Taskasynchronoustaskconst{task}=require('folktale/concurrency/task')const{split,find}=require('lodash/fp')constfs=require('fs')函数读取文件(文件名){returntask(resolver=>{fs.readFile(filename,'utf-8',(err,data)=>{if(err){resolver.reject(err)}resolver.resolve(data)})})}readFile('package.json').map(split('\n')).map(find(x=>x.includes('version')))//执行读取文件.run().listen({onRejected(err){console.log(err)},onResolved(value){console.log(value)}})PointedfunctorPointedfunctor实现了of静态方法,of方法是为了避免使用new创建Object,更深层的意思是of方法将值放入上下文Context中(将值放入容器中,使用map处理值)classContainer{constructor(value){this._value=value}staticof(){returnnewContainer(value)}map(fn){returnnewContainer(fn(this._value))}}Monadfunctor解决了functor嵌套的问题。MonadfunctorisaflattenablePointedfunctorIO(IO),一个functor是一个Monadclass如果它有join和of方法并且遵循一些法则IO{constructor(fn){this._value=fn}staticof(value){returnnewIO(function(){returnvalue})}map(fn){returnnewIO(fp.flowRight(fn,this._value))}join(){returnthis._value()}//同时调用join和mapflatMap(fn){returnthis.map(fn).join()}}functionreadFile(fileName){returnnewIO(()=>fs.readFileSync(fileName,'utf-8'))}functionprint(x){returnnewIO(()=>{returnx})}letr=readFile('package.json').flatMap(print).join()console.log(r)当我们要调用一个返回值的方法时调用map方法,当我们要调用返回函子的方法时调用flatMap方法原文地址:https://kspf.xyz/archives/17更多微信内容公众号搜索小程序填饱肚子