函数式编程小思考4.2 笔记

2019-01-06  本文已影响0人  wudimingwo

函数式编程小思考4 笔记
JS函数式编程指南
Data.Task 函子 源码

补充两个内容

  1. 用得到的curry函数

  2. 文中提及的一些定律,定理什么的

  3. 用得到的curry函数

    var memoize = function(f) {
                var cache = {};

                return function() {
                    var arg_str = JSON.stringify(arguments);
                    cache[arg_str] = cache[arg_str] || f.apply(f, arguments);
                    return cache[arg_str];
                };
            };

            function sub_curry(fn /*, variable number of args */ ) {
                var args = [].slice.call(arguments, 1);
                return function() {
                    return fn.apply(this, args.concat(toArray(arguments)));
                };
            }

            function curry1(fn, length) {
                // capture fn's # of parameters
                length = length || fn.length;
                return function() {
                    if(arguments.length < length) {
                        // not all arguments have been specified. Curry once more.
                        var combined = [fn].concat(toArray(arguments));
                        return length - arguments.length > 0 ?
                            curry(sub_curry.apply(this, combined), length - arguments.length) :
                            sub_curry.call(this, combined);
                    } else {
                        // all arguments have been specified, actually call function
                        return fn.apply(this, arguments);
                    }
                };
            }

            var compose = function() {
                let args = [].slice.call(arguments);
                let len = args.length - 1;
                return function(data) {
                    let result = args[len](data);
                    while(len--) {
                        result = args[len](result);
                    }
                    return result;
                }
            }

            // 应用lodash里的curry
            let curry = _.curry;

            var match = curry(function(what, str) {
                return str.match(what);
            });

            var replace = curry(function(what, replacement, str) {
                return str.replace(what, replacement);
            });

            var filter = curry(function(f, ary) {
                return ary.filter(f);
            });

            var map = curry(function(f, ary) {
                return ary.map(f);
            });
            var split = curry(function(what, str) {
                return split(what, str)
            })
            var reduce = curry(function(f, init, arr) {
                return arr.reduce(f, init)
            })
            var slice = curry(function(start, end, arr) {
                return arr.slice(start, end);
            })

            var toLowerCase = function(str) {
                return str.toLowerCase();
            }

            var join = curry(function(what, arr) {
                return arr.join(what)
            })

            var concat = curry(function(what, str) {
                return str.concat(what)
            })

            var prop = curry(function(prop, obj) {
                return obj[prop]
            })

            var maybe = curry(function(x, f, m) {
                return m.isNothing() ? x : f(m.__value);
            });

            //  map :: Functor f => (a -> b) -> f a -> f b
            var map = curry(function(f, Container) {
                return Container.map(f);
            });

            var id = function(x) {
                return x;
            };

            var of = curry(function(m, x) {
                return m.of(x)
            })

            var trace = _.curry(function(tag, x) {
                console.log(tag, x);
                return x;
            });

            // 第一种容器
            var Container = function(x) {
                this.__value = x;
            }

            Container.of = function(x) {
                return new Container(x);
            };

            // (a -> b) -> Container a -> Container b
            Container.prototype.map = function(f) {
                return Container.of(f(this.__value))
            }

            Container.of("bombs").map(concat(' away')).map(prop('length'))
            //=> Container(10)

            //观察这句代码, 他与 compose 有点类似,
            // compose是从右向左, 而map 是 从左向右
            // compose 是把 return 出来的值, 传递给下一个函数的 参数入口
            // 而 map 是return 一个 容器, 用调用的方式, 把参数传进去.
            // 当然两者有很明显的不同
            // compose 返回的是函数, compose的执行本身 实际上是延迟执行, 预留出一个参数入口
            // 而在上面这种情况, 使用map的时候, 实际上参数值已经确定, 并且功能已经执行,
            // 不存在延迟执行.

            // 这种链式调用方式, 作者似乎称之为 点记法(dot notation syntax)

            // 第二种容器
            var Maybe = function(x) {
                this.__value = x;
            }

            Maybe.of = function(x) {
                return new Maybe(x);
            }

            Maybe.prototype.isNothing = function() {
                return(this.__value === null || this.__value === undefined);
            }

            Maybe.prototype.map = function(f) {
                return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value));
            }

            var maybe = curry(function(x, f, m) { // 出错时返回自定义的信息, 不执行 f, 
                return m.isNothing() ? x : f(m.__value);
            });

            // 第三种容器 Either
            var Left = function(x) {
                this.__value = x;
            }

            Left.of = function(x) {
                return new Left(x);
            }
            // 这又是一种奇特的 map, Left容器遇到多少个map, 多少个 f, 返回的都是同样的值,
            // 让所有f都失效.
            // 这根 Maybe.of(null) 稍微不同.虽然Maybe.of(null) 的 map 也会让所有的map,f都失效.
            Left.prototype.map = function(f) {
                return this;
            }

            var Right = function(x) {
                this.__value = x;
            }

            Right.of = function(x) {
                return new Right(x);
            }

            Right.prototype.map = function(f) {
                return Right.of(f(this.__value));
            }

            //  getAge :: Date -> User -> Either(String, Number)
            var getAge = curry(function(now, user) {
                var birthdate = moment(user.birthdate, 'YYYY-MM-DD');
                if(!birthdate.isValid()) return Left.of("Birth date could not be parsed");
                return Right.of(now.diff(birthdate, 'years'));
            });

            //  either :: (a -> c) -> (b -> c) -> Either a b -> c
            var either = curry(function(f, g, e) {
                switch(e.constructor) {
                    case Left:
                        return f(e.__value);
                    case Right:
                        return g(e.__value);
                }
            });

            //  zoltar :: User -> _
            //          var zoltar = compose(console.log, either(id, fortune), getAge(moment()));
            //
            //          zoltar({
            //              birthdate: '2005-12-12'
            //          });
            //          // "If you survive, you will be 10"
            //          // undefined
            //
            //          zoltar({
            //              birthdate: 'balloons!'
            //          });
            // "Birth date could not be parsed"
            // undefined

            var IO = function(f) {
                this.__value = f;
            }

            IO.of = function(x) {
                return new IO(function() {
                    return x;
                });
            }

            IO.prototype.map = function(f) {
                return new IO(_.compose(f, this.__value));
            }
            var IO = function(f) {
                this.unsafePerformIO = f;
            }

            IO.prototype.map = function(f) {
                return new IO(_.compose(f, this.unsafePerformIO));
            }

            // monad
            Maybe.prototype.join = function() {
                return this.isNothing() ? Maybe.of(null) : this.__value;
            }

            var join = function(m) {
                return m.join();
            }

            // 对其他类型容器都是一样的
            IO.prototype.join = function() {
                return this.unsafePerformIO();
            }

            //  chain :: Monad m => (a -> m b) -> m a -> m b
            var chain = curry(function(f, m) {
                return m.map(f).join(); // 或者 compose(join, map(f))(m)
            });

            Container.prototype.ap = function(other_container) {
                return other_container.map(this.__value);
            }

            // 从 chain/of 衍生出的 map
            X.prototype.map = function(f) {
                var m = this;
                return m.chain(function(a) {
                    return m.constructor.of(f(a));
                });
            }

            // 从 chain/map 衍生出的 ap
            X.prototype.ap = function(other) {
                return this.chain(function(f) {
                    return other.map(f);
                });
            };

            X.prototype.chain = function(f) {
                return f(this.__value)
            }

            X.of = function(x) {
                return new X(x)
            }
            X.prototype.map = function(f) {
                return X.of(f(this.__value))
            }
            X.prototype.ap = function(m) {
                return m.map(this.__value);
            }

            X.prototype.join = function() {
                return this.__value;
            }
            X.prototype.chain = function(f) {
                return this.map(f).join();
            }
  1. 文中提及的一些定律,定理什么的
            /* compose 结合律 */
            /* var associative = compose(f, compose(g, h)) == compose(compose(f, g), h); */

            var id = function(x) {
                return x;
            };

            // identity
            map(id) === id;

            // composition
            compose(map(f), map(g)) === map(compose(f, g));

            /* 任何时候都成立 , 满足单位律*/
            // compose(id, f) == compose(f, id) == f;

            // map 的组合律
            var law = compose(map(f), map(g)) == map(compose(f, g));

            // 同一律
            let id = function(x) {
                return x
            }
            let map1 = curry(function(f, arr) {
                return arr.map(f);
            })
            let map2 = curry(function(f, container) {
                return Container.of(f(container.__value));
            })
            var id1 = map1(id) // 返回的是一个函数
            var id2 = map2(id) // 返回的也是一个函数
            // 没错 无论是id ,id1,id2 哪个函数接收一个容器时, 都会返回同样的容器.
            // 当然 id1 应该接收的是 数组, id2接收的是个容器, 而id 则能接收任何类型

            //同一律
            map(id) === id; // 这两个函数的效果是完全相同的, 但实际上是两个引用值,所以不可能绝对相等.

            // map 的组合律 var law = compose(map(f), map(g)) == map(compose(f, g));

            //          //自由定理 第一遍读的时候,不是很懂, 现在再读一遍, 比第一遍要有所理解, 但还是不太懂.
            // 主要还是适用范围问题.
            // 根据上面 map compose 的h 组合律 就能知道, map这类函数有很特别的地方.

            // head :: [a] -> a 
            compose(f, head) == compose(head, map(f));
            // filter :: (a -> Bool) -> [a] -> [a] 
            compose(map(f), filter(compose(p, f))) == compose(filter(p), map(f));

            // 关于这个map函数, 之前接触数组的map时, 语义上给我感觉是
            // 遍历数组, 打开数组的含义, 使用顺序在感觉上, 是先确定要遍历的数组, 然后再确定要使用的功能
            // 而在curry化之后的 map 给我的语义上的感觉是, 一个函数可以通过map 包装之后,能够打开数组.
            // 这跟我之前自己胡乱总结多层函数嵌套时, 总结出的 函数工厂非常类似,
            // 如果用函数工厂的角度去看,就是, 一个函数经过 map的函数工厂加工后, 可以处理数组了.
            // 相当的, 非常的, 神奇.

            // 当然,我们不要跑题
            // 上面的例子中, 可以看出 head能够打开操作数组, map,filter 也都能打开操作数组.
            // 由此, 我们可以合理猜测, 自由定理的适用条件是, 处理数组时,专用?
            // (还是说, 是之前的猜测, 是因为高阶函数的原因?)
            // 做好标记, 也许看第三遍的时候, 也许能理解的更多.
            
            
              // 结合律
  compose(join, map(join)) == compose(join, join)
  // 这个返回的函数 接收的数据类型, 天然的是两层嵌套以上的容器.
  // 总结果是, 脱了两层容器
  // 左边是先脱里层的,再脱外层的
  // 右边是先脱外层的,再脱里层的
  
    // 同一律 (M a)
  compose(join, of) == compose(join, map(of)) == id
  // 接收一个 容器, 再接收一个值.
  // 
            var of = curry(function (m,x) { // 辅助理解
                return m.of(x)
            })
//              
 
 
   var mcompose = function(f, g) {
    return compose(chain(f), chain(g));
  }
   // 首先, mcompose 接收的参数类型是 容器
   // 其次, 无论是 f,还是g 都会把返回值放进容器当中.
   // 这是使用mcompose的前提条件, 或者也可以认为是 全都使用这种规范的 compose
   
   // 如果只是普通函数, 我们直接用compose
   // 如果函数需要放进容器中, 但返回值是正常值, 我们就选择 IO容器
   // 即IO容器的 map 能够完成compose
   // 但有个要求是, IO容器里的值必须全都是是函数. 任何值或功能,操作都要包裹进函数中.
   
   // 如果 f,g需要把返回值放进容器当中
   // 此时还想用 compose效果, 
   // 就可以用mcompose,
   // mcompose

  // 左同一律
  mcompose(M, f) == f
  // 这个M是什么? 根据上面的用过这个符号来讲, M是一个容器
  // 但根据mcompose来讲, M应该是个函数.

  // 右同一律
  mcompose(f, M) == f
    // 这两个 到底是什么意思
    // 实在是想不出 这个M 到底什么意思

    
  // 结合律
  mcompose(mcompose(f, g), h) == mcompose(f, mcompose(g, h))
  // 这个还算比较好理解
  
  
  F.of(x).map(f) == F.of(f).ap(F.of(x))
  
  // 同一律
  // 此处的v 是一个带有值的 容器
A.of(id).ap(v) == v

// 同态
A.of(f).ap(A.of(x)) == A.of(f(x))

// 互换
v.ap(A.of(x)) == A.of(function(f) { return f(x) }).ap(v)
// 这个很有启发意义有没有?!
// 左边表示, 一个函数找到一个值
// 右边表示, 一个值找到一个函数. 值放进一个函数中, 另一个函数当做参数被调用进来.
// 玩得真是太溜了

// 组合
A.of(compose).ap(u).ap(v).ap(w) == u.ap(v.ap(w));
//例子
var u = IO.of(_.toUpper);
var v = IO.of(_.concat("& beyond"));
var w = IO.of("blood bath ");

IO.of(_.compose).ap(u).ap(v).ap(w) == u.ap(v.ap(w))
//似乎只适用于 IO?
上一篇下一篇

猜你喜欢

热点阅读