函数式编程

范畴与容器

    1、我们可以把“范畴”想象成一个容器,里面包含两样东西,值(value),值的变形关系,也就是函数。
    2、范畴论使用函数,表达范畴之间的关系
    3、伴随着范畴论的发展,就发展出一整套函数的运算方法。这套方法起初只用于数学运算,后来有人将它在计算机上实现了,就编程了今天的函数式编程
    4、本质上,函数式编程只是范畴论的计算方法,跟数学逻辑、微积分、行列式是同一个东西,都是数学方法,只是它碰巧能用来写程序。为什么函数式编程要求函数必须是纯的,不能有副作用?因为它是一种数学运算,原始目的就是求值,不做其他事,否则就无法满足函数运算法则了。
   范畴与容器
        1、函数不仅仅可以用于同一个范畴之中的值转换,还可以用于将一个范畴转成另一个范畴,这就涉及到了函子(functor)
        2、函子是函数式编程里面最重要的数据类型,也是最基本的运算单位和功能单位。它首先是一种范畴,也就是说,是一个容器,包含了值和变形的关系。比较特殊的是,它的变形关系可以依次作用于每一个值,将当前容器变形成另一个容器。
    - 容器、Functor(函子)
        $(...)返回的对象并不是一个原生的DOM对象,而是对于原生对象的一种封装,这种封装,在某种意义上就是一个‘容器’(但它并不是函数式)
        functor(函子)最守一些特定规则和容器类型。
        functor是一个对于函数调用的抽象,我们赋予容器自己去调用函数的能力,把东西装进一个容器,只留出一个接口map给容器外的函数,map一个函数时,我们让容器自己来运行这个函数    ,这样容器就可以自己地选择何时何地如何操作这个函数,以致于拥有惰性求值,错误处理,异步调用等等,非常牛的特性
  var Container=function(value){
                this.__value = value
            }
            //函数式编程一般约定,函子有一个of方法
            Container.of=(x)=>(new Container(x))//Container.of('aaa')
            
            //一般约定,函子的标志就是容器具有map方法。该方法将容器里的每一个值,映射到另一个容器
            Container.prototype.map=function(f){
                return Container.of(f(this.__value))
            }
            Container.of(2)
                .map(x=>x+1)//Container(3)
                .map(x=>console.log("x的值为",x))//Container("x的值为3")
        
  函子的代码实现
        1、任何具有map方法的数据结构,都可以当做函子的实现。
    - map
        下面代码,Functor是一个函子,它的map方法接受函数f作为参数,然后返回一个新的函子,里面包含的值是被f处理过的f(this.val)。
        一般约定,函子的标志就是容器具有map方法。该方法将容器里的每一个值,映射到另一个容器。
        下面的例子说明,函数式编程里面的运算,都是通过函子完成,即运算不直接针对值,而是针对这个值的容器---函子。函子本身具有对外接口(map方法),各种函数就是运算符,通过接口接入容器,引发容器里面的变形。
        因此,学习函数式编程,实际上就是学习函子的各种运算,由于可以把运算方法封装在函数里面,所以有衍生出各种不同类型的函子,有多少种运算,就有多少种函子。函数式编程就变成了运用不同的函子,解决实际问题。
    
          class Functor{
                constructor(val){
                    this._val=val
                }
                map(f){
                    return new Functor(f(this._val))
                }
            }
            (new Functor(3)).map(x=>x+1)//Functor(4)
  of方法
        你可能注意到了,上面生成新的函子的时候,用了new命令。这实在是太不像函数式变成了,因为new命令是面向对象函数的标志。
        函数式编程一般约定,函子有一个of方法,用来新生成的容器。
    
        Functor.of=function(x){
            return new Functor(x)
        }
        Functor.of(2).map(x=>x+1)//Functor(3)

 

    maybe函子
        函子接受各种函数,处理容器内部的值,这里就有一个问题,容器内部的值可能是一个空值(null),而外部函数未必有处理空值的机制,如果传入空值,很可能就会出错。
  
        Functor.of(null).map(x=>x.toUppercase())//TypeError
        class Maybe extends Functor{
            map(f){
                return this._val?Maybe.of(f(this._val)):Maybe.of(null)
            }
        }
        Maybe.of(null).map(x=>x.toUppercase())//Maybe(null)

        var Maybe = function(val){
            this.val = val;
        }
        Maybe.of=function(x){
            return new Maybe(x)
        }
        Maybe.prototype.map=function(f){
            return isNothing()?Maybe.of(f(this.val)):Maybe.of(null)
        }
        Maybe.prototype.isNothing=function(){
            return (!this.val == null)
        }

 

   错误处理、Either
    1、我们的容器能做的事情太少了,try/catch/throw并不是‘纯’的。因为它从外部接管了我们的函数,并且这个函数出错时抛弃了它的返回值。
    Promise是可以调用catch来集中处理错误
    事实上Either并不只是来处理错误的,它表示了逻辑或,范畴学里的coproduc
    - Either
    条件运算符if...else是最常见的运算之一,函数式编程里面,使用Either函子表达。Either函子内部有两个值:左值(left)和右值(right)。右值是正常情况下使用的值,左值是右值不存在的时候使用的默认值。
    
        class Eitch extends Functor{
            constructor(left,right){
                this.left = left;
                this.right = right;
            }
            map(f){
                reutrn this.right?
                Eitch.of(this.left,f(this.right)):
                Eitch.of(f(this.left),this.right)
            }
        }
        Eitch.of = function(left,right){
            return new Eitch(left,right)
        }
        var addOne=function(x){
            return x+1
        }
        Eitch.of(5,6).map(addOne)//Eitch(5,7)
        Eitch.of(5,null).map(addOne)//Eitch(6,null)
        Eitch.of({address:"xxx"},currentUer.address).map(updateField)//代替try...catch

 

    ```//错误处理、Eitch
   
     var Left = function(x){
            this.__value = x
        }
        var Right = function(x){
            this.__value = x
        }
        Left.of=x=>(new Left(x));
        Right.of=x=>(new Right(x));
        Left.prototype.map=function(f){
            return this
        }
        Left.prototype.map=function(f){
            return this
        }
        Right.prototype.map=function(f){
            return Right.of(f(this.__value))
        }

 

        //left和right唯一区别就在与map方法的实现,Right.map的行为和我们之前提到的map函数一样。但是left.map就很不同了:它不会对容器做任何事情,只是很简单地把这个容器拿进来又扔出去。这个特性意味着,Left可以用来传递一个错误信息.
        let getAge = user=>user.age?Right.of(user.age):Left.of("ERROR!")
            getAge({name:"zhangsan",age:12}).map(age=>`Age is ${age}`)//Right(Age is 12)
            getAge({name:"zhangsan"}).map(age=>`Age is ${age}`)//Left("Error")
    ```
        Left可以让调用链中任意一环的错误立即返回到调用链的尾部,这给我们错误处理带来了很大的方便,再也不用一层又一层的try/catch
       AP因子
        1、函子里面包含的值,完全可能是函数。我们可以想象这样一种情况,一个函子的值是数值,另一个函子的值是函子。

            class AP extends Functor{
                ap(f){
                    return AP.of(this._val(f.val))
                }
            }
            AP.of(addTwo).ap(Functor.of(2))

 

 
        IO
        1、真正的程序总是要接触肮脏的世界。
        function readlocalStorage(){
            return window.localStorage();
        }    
        2、IO跟前面那几个Functor不同在于,它是__value是一个函数。它把不纯的操作(比如IO、网络请求、DOM)包裹到一个函数内,从而延迟这个操作的执行。所以我们认为,IO包含的是被包裹的操作的返回值。
        3、IO也算是惰性求值
        4、IO负责了调用链积累了很多很多不纯的操作,带来的复杂性和不可维护性
   
         import _ from 'lodash'
            var compose = _.flowRight;//函数由右至左执行
            var IO = function(f){
                this.__value = f;
            }
            IO.of=x=>new IO(x)
            IO.prototype.map=function(f){
                return new IO(compose(f,this__value))
            }
            //node
            var fs = require("fs");
            var readFile=function(filename){
                return new IO(function(){
                    return fs.readFileSync(filename,"utf-8")
                })
            }
            //flatMap() 返回映射结果
            readFile("../css3d/index.html").flatMap(tail).flatMap(print)
            //同等于

            //chain返回一个lodash实例,该实例包含value启用显示方法链序列的封装
            readFile("../css3d/index.html").chain(tail).chain(print)    
 

转载于:https://www.cnblogs.com/139199228-haicao/p/9122227.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值