函数式编程概述
2019-12-07 本文已影响0人
子皙丶
1. 函数式编程的大概背景
- Javascript作为一种典型的多范式编程语言,这两年随着React Vue的火热,函数式编程的概念也开始更加流行
- Rxjs、loadsh、underscore等多种开源库都使用了函数式的特性
2. 纯函数的定义
-
定义
对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不用依赖外部环境的状态。
举个栗子:
在Js中,对于数组的操作有些是纯的,有些不是纯的。
var arr = [1,2,3,4,5]
//纯函数
arr.slice(0,3) // result : [1,2,3]
arr.slice(0,3) // result : [1,2,3]
//非纯函数
arr.splice(0,3) // result : [1,2,3]
arr.splice(0,3) // result : [4,5]
- 多次调用传入的参数相同会得到相同的结果
- 闭关锁国,不受外部的影响
3. 函数式编程为何排斥不纯的函数
非纯函数中,函数的行为需要由外部的系统环境决定,也就是说此函数行为不仅取决于输入的参数,还取决与外部的变量。
举个栗子:
var timeOfLife = 20
//纯函数
function test(){
return age > 20
}
//非纯函数
function test(){
return age > timeOfLife
}
非纯函数中受外部变量timeOflife的影响,这种对于外部状态的依赖,是造成系统复杂性大大提高的主要原因。
如何解决呢?
4. 函数柯里化
-
定义
向函数传递一部分参数来调用塔,让它返回一个函数去处理剩下的参数。
注:事实上柯里化是一种“预加载”函数的方法,通过传递较少的参数,得到一个已经记住了这些参数的新函数,某种意义上讲,这是一种对参数的“缓存”,是一种非常高效的编写函数的方法。
解决上述代码中的问题:
var timeOflife = 20
function test(timeOflife){
return function(age){
return age > timeOflife
}
}
var testing = test(20)
testing(18) // result : false
5. 函数组合
-
使用函数组合的目的
为了避免写出不优雅的包菜式的代码,例如:h(g(f(x)))
,我们需要用到函数组合
-
定义
定义N个纯函数,像拼积木一样优雅的将N个函数组合起来。
举个栗子:
var compose = function(f,g){
return function(x){
return f(g(x))
}
}
var mult = function(x){
return x * 5
}
var add = function(x){
return x + 1
}
compose(mult,add)(2) // result : 15
- 定义两个纯函数 mult 和 add
- 使用组合函数的方式,将两个纯函数组合成compose,对外调用compose就可以显示功能。
6. 声明式和命令式
-
命令式代码
通过编写一条有一条的命令,让计算机执行一些动作,其中一般会涉及许多繁杂的细节。一般初级编程人员编码时使用比较多。
举个栗子:
var rest = []
var arr = [4,9,16,25]
for(var i = 0 ; i < arr.length ; i++){
if(rest.indexOf(arr[i]) === -1){
rest.push(Math.sqrt(arr[i]))
}
}
函数的目的就是将arr数组中的数据开方放到rest中去,但是用命令式的代码就会发现,代码复杂不易理解,也不够优雅。
-
声明式代码
通过写表达式的方式,声明我们想干什么,而不是通过一步步的指示。声明式代码,相比较函数式编程的一个明显的好处-- 编写简洁、结构优雅、优化代码时更能专注、业务和功能能够分离。
举个栗子:
var arr = [4,9,16,25]
var rest = arr.map(Math.sqrt)
7. 总结
- 函数对于外部状态的依赖,是造成系统复杂性大大提高的主要原因。
- 代码书写中让函数尽可能的纯净。(也就是我们上面说的写纯函数,然后使用组合的方式,将纯函数组合起来已达到实现功能的目的)
- 函数式编程不是万能的,它与OOP一样,只是一种编程规范
- 为降低软件复杂度,OOP的方式是靠良好的封装、继承、多态、以及接口定义。函数式编程则是靠组合、柯里化等技术。
简而言之:函数式编程就是通过柯里化、组合等技术将纯函数灵活运用已达到实现功能的目的。