函数式编程第一章——走近函数式

1.1 什么是函数式编程

函数式编程的目标是使用函数来抽象作用在数据智商的控制流与操作,从而在系统中消除副作用减少对状态的改变

1.2 函数式编程是声明式编程

函数式编程属于声明式编程范式:这种范式会描述一系列的操作,但并不会暴露它们是如何实现的或是数据流如何穿过它们。

目前,更加主流的是命令式的或过程式的编程范式,如Java、C#、C++和其它大多数结构化语言和面向对象语言都对其提供支持。

命令式编程将计算机程序视为一系列自上而下的断言,通过修改系统的各个状态来计算最终的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 命令式具体告诉计算机如何执行某个任务
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i = 0; i < array.length; i++) {
array[i] = Math.pow(array[i], 2);
}
array; // -> [0, 1, 4, 9, ...]

// 声明式将程序的描述与求值分离开来
// 它关注于如何用各种表达式来描述程序逻辑,而不一定要指明其控制流或状态的变化
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(
function(num) {
return Math.pow(num, 2);
}
);

副作用带来的问题和纯函数

函数式编程基于一个前提,即使用纯函数构建具有不变性的程序。纯函数的性质:

  • 仅取决于提供的输入,而不依赖于任何在函数求值期间或调用间隔时可能变化的隐藏状态和外部状态
  • 不会造成超出其作用域的变化,例如修改全局对象或引用传递的参数

副作用:

  • 改变一个全局的变量、属性或数据结构
  • 改变一个函数参数的原始值
  • 处理用户输入
  • 抛出一个异常,除非它又被当前函数捕获了
  • 屏幕打印或记录日志
  • 查询HTML文档、浏览器的cookie或访问数据库

引用透明和可置换性

如果一个函数对于相同的输入始终产生相同的结果,那么就说它是引用透明的。

构建这样的程序更容易推理,因为可以在心中形成一个状态系统的模型,并通过重写替换来达到期望的输出。

存储不可变数据

函数式编程是指为创建不可变的程序,通过消除外部可见的副作用,来对纯函数的声明式的求职过程。

1.3 函数式编程的优点

  • 促使将任务分解成简单的函数
  • 使用流式的调用链来处理数据
  • 通过响应式范式降低事件驱动代码的复杂性

鼓励复杂任务的分解

宏观上将,函数式编程实际上是分解和组合之间的相互作用。

函数式编程的模块化概念与单一职责原则息息相关,也就是说,函数都应该拥有单一的目的。

使用流式链来处理数据

函数链是一种惰性计算程序。这意味着当需要时才会执行。

复杂异步应用中的响应

1
2
3
4
5
6
7
8
9
// Rx.js 函数式响应式编程FRP
Rx.Observable.fromEvent(document.querySelector('#student-ssn'), 'keyup')
.map(input => input.srcElement.value)
.filter(ssn => ssn !== null && ssn.length !== 0)
.map(ssn => ssn.replace(/^\s*|\s*$|\-/g, ''))
.skipWhile(ssn => ssn.length !== 9)
.subscribe(
validSSN => console.log(`Valid SSN ${validSSN}`)
);