参考文档 2
函数式编程术语
高阶函数 Higher-Order Functions
以函数为参数的函数
返回一个函数的函数
函数的元 Arity
比如, 一个带有两个参数的函数被称为二元函数
惰性求值 Lazy evaluation
是一种按需求值机制, 它会延迟对表达式的求值, 直到其需要为止
- // 设置一个随机数, 需要时, 才会计算, 每次计算都是一个不同的值
- const rand = function*() {
- while (1 <2) {
- yield Math.random()
- }
- }
- const randIter = rand()
- randIter.next() // 每个执行都给出一个随机值, 表达式按需求值.
偏函数 Partial Application
即[降元] , 将一个 n 元函数转换成一个 n - x 元函数
或者这样理解, 通过对[复杂的函数] 填充一部分数据来构成一个[简单的函数]
柯里化就是通过偏应用函数来实现
- function add(a, b,c) {
- return a + b+c;
- }
- // 也可以
- var addOne = add.bind(null, 1,2);
- console.log(addOne(2));
- // 也可以
- var addTwo = add.bind(null, 1);
- console.log(addTwo(3,4));
柯里化 Currying
将一个多参数函数转换成多个单参数函数
也就是将一个 n 元函数转换成 n 个一元函数
- const sum = (a, b) => a + b
- const curriedSum = (a) => (b) => a + b
- curriedSum(40)(2) // 42.
- const add2 = curriedSum(2) // (b) => 2 + b
- add2(10) // 12
自动柯里化 Auto Currying
将多个参数的函数转换为单参数的函数
如果, 给定的参数数量少于正确的参数, 则返回一个函数, 该函数将获得其余的参数
如果, 函数得到正确数量的参数时, 它就会被求值
示例, lodash 和 Ramda 都有一个 curry 函数, 但 Underscore 没有
- const add = (x, y) => x + y
- const curriedAdd = _.curry(add)
- curriedAdd(1, 2) // 3
- curriedAdd(1) // (y) => 1 + y
- curriedAdd(1)(2) // 3
compose 组合函数
概念: 它将需要嵌套执行的函数平铺. 嵌套执行指的是, 一个函数的返回值将作为另一个函数的参数
作用: 实现函数式编程中的 pointfree 风格(无参数), 使我们专注于[转换] 而不是[数据]
实现: 接收多个函数作为参数, 从右到左, 一个函数的输入为另一个函数的输出
意义: 编程更精练, 算法更清晰, 无参数干扰
威力:==[任意组合] ==
缺点: 不能直观的看到参数
示例
- var compose = function(fun1,fun2){
- return function(val){
- return fun1(fun2(val));
- }
- }
- var add = function(val){
- return val + "111";
- }
- var upperCase = function(val){
- return val.toUpperCase();
- }
- var double = function(val){
- return val += val;
- }
- // 无限组合, 才是 compose 的威力所在
- var upperCaseThenAdd = compose(add,upperCase);
- var doubleThenAdd = compose(double,add);
- var addThenAdd = compose(add,add);
- var addThenAddThenUpperCase = compose(upperCase,addThenAdd);// 注意这个函数, 以组合函数 addThenAdd 作为其参数, 很强大, 有没有!
- console.log(upperCaseThenAdd("china"));//CHINA111
- console.log(doubleThenAdd("china"));//china111china111
- console.log(addThenAdd("china"));//china111111
- console.log(addThenAddThenUpperCase("china"));//CHINA111111
- // 改进 compose, 支持 2 个以上参数
- var compose1 = function(){
- var args = arguments;
- return function(initVal){
- var val = initVal;
- for(key in args){
- val = args[key](val);
- }
- return val;
- }
- }
- var doubleThenUpperCaseThenAddThenAdd = compose1(double,upperCase,add,add);
- console.log(doubleThenUpperCaseThenAddThenAdd("china"));//CHINACHINA111111
- Continuation
概念: 在一个程序执行的任意时刻, 尚未执行的代码称为 Continuation
作用: 异步请求回调, 异步监听回调等
示例
- //continueFun 函数就称为一个 Continuation
- var addOneAndContinue = function(val,continueFun){
- var val = val + 1;
- return continueFun(val);
- }
- var mutiply = function(val){
- return val * 5;
- }
- console.log(addOneAndContinue(100,mutiply));//505
纯函数 Purity
输出仅由输入决定, 不依赖也不修改外部状态, 即不产生副作用
副作用 Side effects
如果函数与外部可变状态进行交互, 则它是有副作用的
幂等性 Idempotent
数学中的幂等性
foo(x) 将产生与 foo(foo(x)),foo(foo(foo(x))) 等相同的输出
[二元运算], 它需要三个元素: 二元运算符以及该运算符作用的两个变量. 如四则运算的加, 减, 乘, 除均属于二元运算. 乘法下唯一两个幂等实数为 0 和 1
[一元运算], 例如 ++ , 正 +, 负 -. 比如[高斯符号], 它是一个数学符号, 形式为方括号[x], 表示不大于 x 的最大整数, 高斯符号是幂等的
接口的幂等性
对接口而言, 幂等性实际上就是接口可重复调用, 在调用方多次调用的情况下, 接口最终得到的结果是一致的. 比如, 在 App 中下订单的时候, 点击确认之后, 没反应, 就又点击了几次. 在这种情况下, 如果无法保证该接口的幂等性, 那么将会出现重复下单问题
[http 方法的幂等], 指的是同样的请求被执行一次与连续执行多次的效果是一样的, 服务器的状态也是一样的(注意, 只是服务器状态, 和服务器返回状态无关)
举例
GET /pageX HTTP/1.1 是幂等的. 连续调用多次, 客户端接收到的结果都是一样的:
- GET /pageX HTTP/1.1
- GET /pageX HTTP/1.1
- GET /pageX HTTP/1.1
- GET /pageX HTTP/1.1
POST /add_row HTTP/1.1 不是幂等的. 如果调用多次, 就会增加多行记录:
- POST /add_row HTTP/1.1
- POST /add_row HTTP/1.1 -> Adds a 2nd row
- POST /add_row HTTP/1.1 -> Adds a 3rd row
DELETE /idX/delete HTTP/1.1 是幂等的, 即便是不同请求之间接收到的状态码不一样:
- DELETE /idX/delete HTTP/1.1 -> Returns 200 if idX exists
- DELETE /idX/delete HTTP/1.1 -> Returns 404 as it just got deleted
- DELETE /idX/delete HTTP/1.1 -> Returns 404
程序的幂等性
概念: 一个函数执行多次皆返回相同的结果
作用: 一个函数被调用多次时, 保证内部状态的一致性
对比: 和纯函数相比, 幂等主要强调多次调用, 对内部的状态的影响是一样的(但多次调用返回值可能不同). 而纯函数, 主要强调相同的输入, 多次调用, 输出也相同且无副作用.== 纯函数一定是幂等的 ==
意义: 在任何可能的情况下通过幂等的操作限制副作用要比不做限制的更新要好得多. 确保操作是幂等的, 可避免意外的发生
- // 虽然是一个面向对象的例子, 但是可以说明问题
- var Student = function(name,age){
- this.name = name;
- this.age = age;
- };
- Student.prototype.delName = function(){
- var response = this.name ? this.name + "已被删除":"name 不存在";
- this.name = null;
- return response;
- }
- // 对内部的影响是一样的, 但是返回值可以不同
- var lilei = new Student("lilei",19);
- console.log(lilei.delName());//lilei 已被删除
- console.log(lilei.delName());//name 不存在
- console.log(lilei.delName());//name 不存在
Point-Free 风格
定义函数时, 不显式地指出函数所带参数. 这种风格通常需要柯里化或者高阶函数. 也叫 Tacit programming
断言函数 Predicate
根据输入返回 true 或 false. 通常用在 Array.prototype.filter 的回调函数中.
- const morethenTwo = (a) => a> 2;
- ;[1, 2, 3, 4].filter(morethenTwo);
契约 Contracts
契约保证了函数或者表达式在运行时的行为. 当违反契约时, 将抛出一个错误
比如数据类型检测
- const contract = (input) => {
- if (typeof input === 'number') return true
- throw new Error('Contract Violated: expected int -> int')
- }
- const addOne = (num) => contract(num) && num + 1
- addOne(2)
- addOne('hello') // Error
范畴 Category
[不好理解]
范畴是指, 对象 (object) 及它们之间的态射(箭头, 箭头可以组合)
在程序中, 数据类型作为对象, 函数作为态射
[一个范畴遵从三个原则]
必有一个态射(函数), 使得 map 一个对象是它自身
态射 (函数) 必是可组合的
合成满足结合律. f ? (g ? h) 与 (f ? g) ? h 是等价的
态射 morphism
某一范畴中, 对象之前的变换关系(一个变形的函数)
函子 functor(范畴学的内容)
一个实现 map 函数的对象
在 JavaScript 中一个常见的函子是 Array, 因为它遵守因子的两个准则
一致性 Preserves identity, 即范畴的第一个原则
组合性 Composable
示例
- // 一致性
- object.map(x => x) ? object
- // 组合性
- var fun1 = function(x){
- return x+1;
- }
- var fun2 = function(x){
- return x*x;
- }
- var res1 = [1,2,3].map(fun1).map(fun2);
- var res2 = [1,2,3].map(function(x){
- return fun2(fun1(x));
- });
- console.log(res1,res2);
- Pointed Functor
一个具有 of 函数的对象, 它将 任何 单独的值放入其中
ES6 增加了 Array.of , 使数组成为一个 Pointed Functor
Array.of(1) // [1]
引用透明性 Referential Transparency
定义: 一个表达式在程序中可以被它等价的值替换, 而不影响结果
对函数而言: 如果函数的返回值只依赖于其输入值, 这种特性就称为引用透明性
== 纯函数具有引用透明性 ==
等式推理 Equational Reasoning
指当应用程序由表达式组成, 并且没有副作用时, 关于系统的真值可以从各个部分推导出来
纯函数式语言的优点之一是易于进行等式推理, 通过引用透明度实现, 并且能够在所有上下文中用等号替换 equals
不可变性
比如 es6 中的 const 常量设计
匿名函数 Lambda
匿名函数往往被视作一个值
匿名函数通常作为高阶函数的参数
可以把 Lambda 赋值给一个变量
Monad 对象
拥有 of 和 chain 函数的对象. chain 很像 map, 除了用来铺平嵌套数据
示例, 以数组来实现
- //of
- Array.of(1,2,3);//[ 1, 2, 3 ]
- //chain 方法的实现
- Array.prototype.chain = function (f) {
- return this.reduce((acc, it) => acc.concat(f(it)), [])
- };
- Array.of('cat,dog', 'fish,bird').chain(s => s.split(','));//[ "cat", "dog", "fish", "bird" ]
Comonad 对象
拥有 extract 与 extend 函数的对象
自同态 Endomorphism
输入输出是相同类型的函数
示例:
- // uppercase :: String -> String
- const uppercase = (str) => str.toUpperCase()
- // decrement :: Number -> Number
- const decrement = (x) => x - 1
- Applicative Functor
一个拥有 ap 函数的对象
同构 Isomorphism
不用类型对象的变形, 保持结构并且不丢失数据
例如, 一个二维坐标既可以表示为数组 [2, 3], 也可以表示为对象 {x: 2, y: 3}
- // 提供函数在两种类型间互相转换
- const pairToCoords = (pair) => ({
- x: pair[0], y: pair[1]
- })
- const coordsToPair = (coords) => [coords.x, coords.y]
- console.log(pairToCoords([1, 2]));//{
- "x": 1, "y": 2
- }
- console.log(coordsToPair({
- x: 1, y: 2
- }));//[ 1, 2 ]
Setoid 对象
定义: 拥有 equals 函数的对象. equals 可以用来和其它对象比较.
- Array.prototype.equals = function (arr) {
- const len = this.length
- if (len !== arr.length) {
- return false
- }
- for (let i = 0; i <len; i++) {
- if (this[i] !== arr[i]) {
- return false
- }
- }
- return true
- }
- ;[1, 2].equals([1, 2]) // true
- ;[1, 2].equals([3, 4]) // false
半群 Semigroup
定义: 一个拥有 concat 函数的对象. concat 可以连接相同类型的两个对象
示例: 比如 Array 具有 concat 方法
Foldable 对象
定义: 一个拥有 reduce 函数的对象, reduce 可以把一种类型的对象转化为另一种类型
示例: 将一个 list 转为 number
- var sum = [1,2,3,4].reduce(function(total,val){
- return total += val;
- })
- console.log(sum);
类型签名 Type Signatures
一种注释方式
- // 通常 JS 会在注释中指出参数与返回值的类型
- // functionName :: firstArgType -> secondArgType -> returnType
- // add :: Number -> Number -> Number
- const add = (x) => (y) => x + y
- // increment :: Number -> Number
- const increment = (x) => x + 1
- // 如果函数的参数也是函数, 那么这个函数需要用括号括起来.
- // call :: (a -> b) -> a -> b
- const call = (f) => (x) => f(x)
- // 字符 a, b, c, d 表明参数可以是任意类型. 以下版本的 map 的参数 f, 把一种类型 a 的数组转化为另一种类型 b 的数组.
- // map :: (a -> b) -> [a] -> [b]
- const map = (f) => (list) => list.map(f)
代数数据类型 Algebraic data type
由其他类型组合在一起的复合类型. 两种常见的代数类型是 sum 和 product
联合类型(对象) Union Type
定义: 连接不同的数据类型
示例: add 就是一个联合类型对象, 因为 JS 天然支持 number 和 sting 求和时, 进行自动数据类型转换
- // add :: (NumOrString, NumOrString) -> NumOrString
- const add = (a, b) => a + b
- add(1, 2) // Returns number 3
- add('Foo', 2) // Returns string "Foo2"
- add('Foo', 'Bar') // Returns string "FooBar"
- Product type
定义: 用一种你可能更熟悉的方式把数据类型联合起来
- // point :: (Number, Number) -> {
- x: Number, y: Number
- }
- const point = (x, y) => ({
- x: x, y: y
- })
Sum 类型(有时称为联合类型 )
是将两种类型的组合合并成另一种类型
之所以被称为 sum , 是因为结果类型中可能的值的数量是输入类型的总和
JavaScript 没有这样的类型, 但是我们可以使用 Set 来假装
- // 想象一下, 在这里我们不能设置只能具有这些值的类型
- const bools = new Set([true, false])
- const halfTrue = new Set(['half-true'])
- // 弱逻辑类型包含 bools 和 halfTrue 值的总和
- const weakLogicValues = new Set([...bools, ...halfTrue])
- Option | maybe
Option 是一种 sum type , 它有两种情况, Some 或者 None.
Option 对于组合可能不返回值的函数很有用
在其它的一些地方, Option 也称为 Maybe,Some 也称为 Just,None 也称为 Nothing
来源: https://www.cnblogs.com/zk995/p/10400555.html