Javascript的函数式编程术语解释
函数式编程(functional programming)很多术语来自于组合数学或范畴理论,下面使用Javacript解释这些术语的含义:
Arity数量
这是代表一个函数的参数数量,从1 2 3数量的单词一次是unary(一元)、binary(二元) ternary(三元)等等,这些单词都有后缀:"-ary"或"-ity",如果一个函数带有两个参数称为binary函数,一个函数带许多可变参数称为"variadic,",而一个binary函数有且只有两个参数。
const sum = (a, b) => a + b; const arity = sum.length; console.log(arity);
// => 2 //sum的参数arity数量是2
高阶函数(HOF:Higher-Order Function)
一个函数将函数作为参数,并且/或者返回也是一个函数,这个函数称为高阶函数:
const filter = (pred, xs) => {
const result = [];
for (var idx = 0; idx < xs.length; idx += 1) {
if (pred(xs[idx])) {
result.push(xs[idx]);
}
}
return result;
};
该函数调用:
filter(is(Number), [0, '1', 2, null]); //=> [0, 2]
其中is函数是作为filter的参数函数:
const is = type => x => Object(x) instanceof type;因此,filter函数结果是[0,2],这个结果可以不是一个函数。
Partial应用
对一个有多个参数的函数,如果我们从其只获得更少参数的函数,这个过程处理称为partial应用.
let sum = (a, b) => a + b;
// partially只应用了 `a`参数为 `40` ,'b'参数没有使用
let partial = sum.bind(null, 40);
// 现在使用了`b` 参数
partial(2); //=> 42
注意,Javascript的bind call和apply都是类似,其第一个参数被绑定的函数,这里设为null,没有使用,后面开始按照函数的参数固定顺序对应了。
Currying柯里化
转换一个带有多个参数的函数到同样的函数但是只带有一个参数了,不要和partial应用混淆,后者能产生多于一个参数的函数。
let sum = (a, b) => a + b;
let curriedSum = (a) => (b) => a + b;
curriedSum(40)(2) // 42.
Composition组合
结合两个给定类型(某种函数)的值变成同样类型的第三方值。
组合最直接类型称为"正常函数组合".允许你组合多个只有一个单值参数和只返回一个单值的函数。
const compose = (f, g) => a => f(g(a)) // 组合定义,定义了f(g(a))两个组合
const floorAndToString = compose((val)=> val.toString(), Math.floor) //使用组合
floorAndToString(121.212121) // "121"
Purity纯洁
一个函数定义为纯函数:如果函数返回值仅仅由其输入值决定,而没有任何副作用。
let greet = "yo ";
greet.toUpperCase(); // YO;
greet // yo;
上述greet是纯的,因为其输出由输入决定。相反案例:
let numbers = [1, 2, 3];
numbers.splice(0); // [1, 2, 3]
numbers // []
这里numbers函数输入是[1,2,3],numbers.splice(0)是一个有副作用的操作,改变了numbers内部状态,导致numbers输出改变。
副作用Side effect
一个函数或表达式如果出现下面情况被认为有副作用:除了返回一个值,它还修改了内部状态,或者有一个和外部函数有一个能够被观察的交互,所谓能够被观察,也就是能被外界植入改变状态的可能,提供了改变内部状态的可能。一般IO操作是有副作用的。
幂等Idempotency
能够多次使用同样的输入参数无副作用地执行多次。
f(f(x)) = f(x)
Math.abs(Math.abs(10))
Point-Free 风格
并没有显式定义参数的函数,通常需要柯里化和高阶函数
// 假定如果 let map = fn => list => list.map(fn); let add = (a, b) => a + b;
// 那么 // Not points-free - `numbers`是显式参数 let incrementAll = (numbers) => map(add(1))(numbers);
// Points-free - 这个集合list是隐式参数 let incrementAll2 = map(add(1));
incrementAll是使用参数numbers,它就不是points-free,而incrementAll2是有函数和返回值,但是没有提到它的参数,因此它是points-free
points-free函数定义了像没有函数或=>的正常赋值,其实这是定义了一个函数。
以上是有关组合数学的相关术语,范畴相关术语见下一页。