• 函数式编程思维
  • 函数式编程常用核心概念
  • 当下函数式编程最热的库
  • 函数式编程的实际应用场景

理论 - 图1

函数式编程思维

范畴

  • 彼此之间存在某种关系概念、事务、对象等等,都构成范畴。
  • 任何事物,只要找出他们之间的关系,就能定义。
  • 箭头表示范畴成员之间的关系,正式名称“态射”。
  • 同一个范畴的所有成员,就是不同状态的“变形”。即通过“态射”,一个成员可以变形成另一个成员。

总结

  1. 所有成员是一个集合
  2. 变形关系是函数
  3. 忘却掉if else
  4. 以数学思维考虑

关键字:态射 变形

函数式编程基础理论

  • 给定x只会输出唯一的y。
  • 将复杂函数符合成简单的函数。(嵌套的函数调用)。
  • function xx(){}这是函数,方法是与指定的对象绑定,函数可直接调用。
  • 基础模型来源于λ(lambda x=>x*2)。λ是在20世纪三十年代引入的一套用于研究函数定义、函数定义、函数应用和递归的形式系统。
  • Javascript是披着C外衣的lisp(适用于符号处理、自动推理、硬件描述和超大规模集成电路设计等)
  • React的高阶函数

总结

  1. 函数是一等公民。“第一等公民”,函数与其他数据类型一样,处于平等地位,柯赋值给其他变量也可以作为参数,传入另一个函数,或者作为别的函数的返回值。即可传、可执行、可返回。
  2. 只用表达式,不用语句(if else)。
  3. 没有副作用,即该函数只简单的返回了一个值,没有进行别的操作(修改变量、抛出异常)。
  4. 不可改变量,意味着类似于const 定义的变量,只能被赋值一次。
  5. 函数式编程常用方法:map、reduce。
  6. 引用透明(参数相同,输入相同,输出相同)。替换模型:identity=(i)=>{return i} identity(1)可替换为1

关键字:一等公民 副作用 引用透明

函数式编程常用核心概念

纯函数

相同输入,固定输出,没有任何可观察的副作用,也不依赖外部环境的状态

例如

slice() 是纯函数;splice()不是。

优点

降低有效系统的复杂度,还有可缓存性等很棒的特性。(lodash 类库帮助)

缺点
  1. //不纯的
  2. var min = 18;
  3. var checkage = age => age > min; //checkage取决于age和min
  4. //纯的
  5. var checkage = age => age > 18; //checkage取决于age
  6. //关键数字18硬编码在函数内部,扩展性差,柯里化可以优雅的解决(柯里化定义见下方)
  7. var checkage = min => (age=>age>min);
  8. var checkage18 = checkage(18);
  9. checkage18(20);

纯度和幂等性

  • 幂等性:执行无数次后还具有相同的效果,同一的参数运行一次函数应该与连续两次结果一致,比如求绝对值。
  • 幂等性在函数式编程中与纯度有关,但有不一致。比如求反。

偏应用函数

  • 指定部分参数,返回 一个函数去处理剩下的参数。比如Promise .then bind。
  • “偏”,只能处理那些能与至少一个case语句匹配的输入,而不能处理所有可能的输入。

示例
  1. const partial = (f,...args)=>(moreArgs)=>f(...args,...moreArgs);
  2. const add=(a,b,c)=>a+b+c;
  3. const fivePlus=partial(add,2,3);
  4. fivePlus(4)
  5. //bind实现
  6. const add1More = add.bind(null,2,3);//(c)=>2,3,c

遗留问题

bind是如何实现的?

函数的柯里化

偏应用函数的细节实现。把一个多参数函数转换为一个嵌套的一元函数的过程。 我的理解是先将参数一步一步的传入,直到所有参数齐全,然后获得最后的值

反柯里化

目的是为了扩大使用范围。

示例

柯里化

  1. // 柯里化之前
  2. function add(x, y) {
  3. return x + y;
  4. }
  5. add(1, 2) // 3
  6. // 柯里化之后
  7. function addX(y) {
  8. return function (x) {
  9. return x + y;
  10. };
  11. }
  12. addX(2)(1) // 3

反柯里化

  1. Function.prototype.uncurring = function() {
  2. var self = this;
  3. return function() {
  4. var obj = Array.prototype.shift.call(arguments);
  5. return self.apply(obj, arguments);
  6. };
  7. };
  8. var push = Array.prototype.push.unCurrying(),
  9. obj = {};
  10. push(obj, "first", "two");
  11. console.log(obj);

优点

“预加载”,通过传递较少的的参数,得到一个已经记住了这些参数的新函数。比如fetch

缺点

洋葱式代码

柯里化与偏应用函数的区别

  • 柯里化的参数列表是从左到右,如果使用setTimeout需要额外封装
  • curry和partial参考Lodash

    函数组合

    为了解决函数嵌套的问题,需要用到“函数组合”

  1. const compose = (f,g)=>(x=>f(g(x)));
  2. var first = arr => arr[0];
  3. var reverse = arr => arr.reverse();
  4. var last = compose(first,reverse);
  5. last([1,2,3,4,5]);
  • compose函数只能组合接受一个参数的函数; filter map接受两个参数 (投影函数:总是在应用转换操作,通过传入高阶函数后返回数组),不能被直接组合可以借助偏函数包裹后继续组合;
  • 函数组合的数据流是从右到左,最右边的函数首先执行,如果需要从左执行呢?可以实现pipe(管道或者叫序列)
  • 通过组合子交换数据方向

    组合子

    管理函数程序执行流程,并在链式调用中对中间结果进行操作。 不声明任何变量,不包含任何业务逻辑。

常用组合子

image.png

Point Free

把一些对象自带的方法转化为纯函数,不要命名转瞬即逝的中间变量 ,减少不必要的命名

  1. const f = str => str.toUpperCase().split('');
  2. //str作为中间变量除了代码变长没意义
  3. var toUpperCase = word => word.toUpperCase();
  4. var split = x => (str => str.split(x));
  5. var f = compose(split(' '), toUpperCase);
  6. f("abcd efgh");
  7. //减少了不必要的命名

声明式与命令式代码

命令式代码:编写一条又一条指令让计算机执行一些动作; 用写表达式的方式来声明我们想干什么。

image.png

优点

声名式的代码,对于无副作用的纯函数,不用考虑其内部是如何实现的,专注于业务代码即可,所以优化代码时,只需要集中在函数内部。

缺点

使用不纯的函数时需要考虑副作用或者外部系统环境。

类SQL数据:函数即数据

image.png

惰性链、惰性求值、惰性函数



    1. 惰性链:添加一个输入对象的状态,从而能够将这些输入转换为所需的输出操作链接在一起<br /> 用到时再执行,避免创建任何变量,有效消除所有循环<br /> 惰性求值:当输入很大但只有一个小的子集有效时,避免不必要的函数调用 尽可能推迟求值,直到依赖的表达式被调用 || ppt 37 尝试

专业术语

  • 高阶函数 函数当参数,把传入的函数做一个封装,然后返回这个封装函数,达到更高程度的抽象

    1. 一等公民、以一个函数作为参数,以一个函数作为返回
  • 尾调用优化PTC 尾调用 递归,就是函数调用自身,尾调用自身,就是尾递归,递归需要大量的调用记录,很容易发生栈溢出错误,如果使用尾递归优化,将递归变为循环,那么就只保存一个调用记录; 谁出来谁又出去

    蹦床函数 解决了递归问题

  • 闭包 会对内存产生影响 函数执行完毕以后,栈上的调用帧被释放,堆上的作用域并不被释放,闭包就是在堆上,除非调用GC

  • 容器,有valu就是个容器、容器会升级成Functor函子 vue的reduce 能作用于自己的变形关系就是函子 map

    拆箱 装箱
    Pointed函子的子集 of 生成新的容器
    Maybe函子用来处理错误和异常
    Either函子 处理左值 右值
    AP函子 value可能是函数 帮你执行

  • 错误处理、Either、AP

  • IO 把不纯的操作 包裹到一个函数内 网络请求,DOM,从而延迟这个操作的执行
  • Monad 是一种设计模式,表示将一个运算过程,通过函数拆解成互相连接的多个步骤 Promise

loadish rx