函数式编程小思考4.2 笔记
2019-01-06 本文已影响0人
wudimingwo
函数式编程小思考4 笔记
JS函数式编程指南
Data.Task 函子 源码
补充两个内容
-
用得到的curry函数
-
文中提及的一些定律,定理什么的
-
用得到的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();
}
- 文中提及的一些定律,定理什么的
/* 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?