来自 Professor Frisby’s Mostly Adequate Guide to Functional Programming
英文版本
中文版本-版本较老
函数式编程是一种编程范式
, 所谓范式, 就是一种编程规范
类与对象
, 通过封装, 继承, 多态来表示事物之间的联系这里的函数指的是映射 形如 y = x 2 y = x^2 y=x2
思考: 一个函数, 参数在另一个函数中调用 是否可以直接将另一个函数拿过来?
形如以下函数
args => actualCallFunction(args)
❗️ 只会徒增代码量 这种形式等价于 actualCallFunction
const log = arg => console.log(arg)
const a = arg => log(arg)
// a 和以下代码等价
const b = log
a(123) // 123
b(456) // 456
再看个
const actualCallFunction = _ => console.log('qweasdzxc')
const foo = aFunction => actualCallFunction(arg => aFunction(arg))
// 优化一下
const foo2 = aFunction => actualCallFunction(aFunction)
// 接着优化
const foo3 = actualCallFunction
foo() // qweasdzxc
foo2() // qweasdzxc
foo3() // qweasdzxc
纯函数
: 相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用
副作用
中的“副”是滋生 bug 的温床
副作用可能包含,但不限于:
curry
:只需传给函数一些参数,就能得到一个新函数
const add = x => y => x + y;
const increment = add(1);
const addTen = add(10);
console.log(increment(2)) // 3
console.log(addTen(2)) // 12
一个简单的 compose 函数如下
const compose = (f, g) => x => f(g(x));
可以看到, 代码执行顺序是从右向左
并且, 组合函数遵循结合律
// associativity
compose(f, compose(g, h)) === compose(compose(f, g), h);
举个 咯
const compose = (f, g) => x => f(g(x));
const toUpperCase = x => x.toUpperCase()
const excalim = x => x + '!'
const logMidRes = x => console.log(x) || x
const convertString = compose(excalim, toUpperCase)
const convertString2 = compose(compose(logMidRes, excalim), compose(logMidRes, toUpperCase))
const a = convertString('welcome')
const b = convertString2('welcome') // WELCOME WELCOME!
console.log(a) // WELCOME!
console.log(b) // WELCOME!
函数不会提到其操作的数据, 如下
// not pointfree because we mention the data: name
const initials = name => name.split(' ').map(compose(toUpperCase, head)).join('. ');
// pointfree
const initials2 = compose(intercalate('. '), map(compose(toUpperCase, head)), split(' '));
const a = initials('hunter stockton thompson'); // 'H. S. T'
const b = initials2('hunter stockton thompson'); // 'H. S. T'
console.log(a)
console.log(b)
以下为工具代码
// args 为实际使用数据 fn , 此时为数组, 展开(...)之后, 被 fn 调用, 调用后将返回的结果放入一个数组中, 以便下次可展开调用, 最后, 取出数组中唯一的值
const compose = (...fns) => (...args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];
function curry(fn) {
const arity = fn.length;
return function $curry(...args) {
if (args.length < arity) { // 参数未达到使用条件, 如 split(' '), 添加操作数并返回
return $curry.bind(null, ...args);
}
// 实际调用, 传入实际使用参数
return fn.call(null, ...args);
};
}
// give it a sep and we get a function back => waiting for its str argument.
const split = curry((sep, str) => str.split(sep));
const intercalate = curry((str, xs) => xs.join(str));
const map = curry((fn, f) => f.map(fn));
const toUpperCase = x => x.toUpperCase()
const head = x => x[0];