Aitter's Blog

函数式编程之认识函数式编程

易有太极,始生两仪,两仪生四象,四象生八卦,函数就相当于太极,组合时灵活多变,但函数本身是不变的

解释

函数式编程是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)

是一种”编程范式”(programming paradigm),也就是如何编写程序的方法论

它属于”结构化编程”的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。

函数式编程通过使用函数来将值转换成抽象单元,接着用于构建软件系统。

函数

函数就是A到B的关系映射

数学上的定义是:
在一个变化过程中,有两个变量x、y,如果给定一个x值,相应的就确定唯一的一个y,那么就称y是x的函数,其中x是自变量,y是因变量,x的取值范围叫做这个函数的定义域,相应y的取值范围叫做函数的值域。

面向过程编程

是一种以过程为中心的编程思想。“面向过程”也可称之为“面向记录”编程思想,他们不支持丰富的“面向对象”特性(比如继承、多态),并且它们不允许混合持久化状态和域逻辑。

面向对象编程

按人们认识客观世界的系统思维方式,采用基于对象(实体)的概念建立模型,模拟客观世界分析、设计、实现软件的办法。通过面向对象的理念使计算机软件系统能与现实世界中的系统一一对应。

具有相同特性(数据元素)和行为(功能)的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象,类实际上就是一种数据类型。

对象

对象是人们要进行研究的任何事物,从最简单的整数到复杂的飞机等均可看作对象,它不仅能表示具体的事物,还能表示抽象的规则、计划或事件。

类和对象,其实都是从数据角度出发来思考和解决问题,以数据本身为运算核心来抽象我们的计算行为

很多时候,我们会发现,其实我们的运算行为远远比数据本身要复杂,而且,我们很多时候,其实并不能很好的去抽象一个对象

所谓程序,就是数据结构加算法。

面向函数编程

所谓面向函数就是使用函数来作为我们分析和抽象程序的主要工具

面向对象是从数据结构的角度出发的话,面向函数的编程,就是从算法角度出发,也就是从行为的角度出发

面向对象编程

function Man(sexy){
var self = this;
self._sexy = sexy;
self.sayHello = function(string){
console.log("I'm a "+self._sexy+",I want to say:"+string);
}
}
var male = new Man("man");
male.sayHello('my name is homker');

面向函数编程

function Man(sexy){
return function(string){
console.log("I'm a "+sexy+",I want to say:"+string);
}
}
var sayHello = Man('man');
sayHello('my name is homker');

在函数式编程中,我们去除掉了主语。你不知道这个动作是由谁发出的。相比于在面向对象编程中,数据是对象的属性,在函数式编程中,我们并不在乎这个数据的内容是什么,而更在乎其变化。

为什么使用函数式编程?

在实际的开发过程中,我们有的时候很难抽象出一个对象来描述我们到底要做什么,或者说,我们其实并不在乎这堆数据里面的内容是什么,我们要关心的,只是把数据经过加工,得出结果,仅此而已。至于这个数据,到底是用来干什么的,我们其实可以并不用关心。

特点

1. 函数是”第一等公民”

和其它数据类型一样,可以做为函数的参数,也可以作用函数的返回值

  • 函数可以存储为变量
  • 函数可以成为数组的一个元素
  • 函数可以成为对象的成员变量
  • 函数可以在使用的时被直接创建
  • 函数可以被作为实参传递
  • 函数可以被另一个函数返回
  • 函数可以返回另一个函数
  • 函数可以作为形参

作为参数

const printFn = a => console.log(a)
[1,2,3].forEach(printFn)

作为返回值

const add = function (x) {
return function (y) {
return x + y
}
}
// ES6
const add = x => y => x+y

2. 只用”表达式”,不用”语句”

表达式:一个单纯的运算过程,总有返回值
语句:执行一个操作,没有返回值

3. 没有”副作用”

函数的执行只返回新值,没有其它行为,不会改变外部环境

4. 不修改状态

函数的执行只返回新的值,不修改传入参数

5. 引用透明

函数的运行不依赖于外部变量或”状态”,只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的

意义

1. 代码简洁,开发快速

使用大量可复用的函数开发,减少了代码的重复,因此程序比较短,开发速度较快

2. 接近自然语言,易于理解

函数式编程的自由度很高,可以写出很接近自然语言的代码

add(1,2).multiply(3).subtract(4)
merge([1,2],[3,4]).sort().search("2")

3. 更方便的代码管理

相同的参数总是返回固定的结果,便于调试和单元测试,以及模块组合

4. 易于”并发编程”

因为不会修改变量,执行环境独立,所以也不需要考虑”死锁”,适合并发编程

5. 代码的热升级

函数式编程没有副作用,只要保证接口不变,内部实现是外部无关
所以,可以在运行状态下直接升级代码,不需要重启,也不需要停机

总结

在实际的操作过程中,我们或多或少的都使用了函数式编程的一部分,我们或多或少的都在践行函数式编程的理论,但是,如果说,我们的代码就是使用函数式编程的时候,我们又会发现,我们的代码中,有很大一部分的逻辑,实在是没办法使用函数式编程进行处理。所以,后面有了响应式编程RXJs,通过订阅和发布模式来实现队列化的事件调度和资源分配,但是在实际使用过程中,要想很快的将代码转化成函数式编程,需要对行为逻辑有很深刻的理解和抽象,对步骤的分解,对函数的分解,对行为的分解,这个才是函数式编程中最难的部分,如何去思考你的数据发生了什么变化,你的状态发生了什么变化,去管理你的数据和你的状态。