Board logo

标题: JavaScript 中的函数式编程实践(1)基础知识 [打印本页]

作者: look_w    时间: 2018-9-14 17:09     标题: JavaScript 中的函数式编程实践(1)基础知识

基础知识函数式编程简介说到函数式编程,人们的第一印象往往是其学院派,晦涩难懂,大概只有那些蓬头散发,不修边幅,甚至有些神经质的大学教授们才会用的编程方式。这可能在历史上的某个阶段的确如此,但是近来函数式编程已经在实际应用中发挥着巨大作用了,而更有越来越多的语言不断的加入诸如 闭包匿名函数等的支持,从某种程度上来讲,函数式编程正在逐步“同化”命令式编程。
函数式编程思想的源头可以追溯到 20 世纪 30 年代,数学家阿隆左 . 丘奇在进行一项关于问题的可计算性的研究,也就是后来的 lambda 演算。lambda 演算的本质为 一切皆函数,函数可以作为另外一个函数的输出或者 / 和输入,一系列的函数使用最终会形成一个表达式链,这个表达式链可以最终求得一个值,而这个过程,即为计算的本质。
然而,这种思想在当时的硬件基础上很难实现,历史最终选择了同丘奇的 lambda 理论平行的另一种数学理论:图灵机作为计算理论,而采取另一位科学家冯 . 诺依曼的计算机结构,并最终被实现为硬件。由于第一台计算机即为冯 . 诺依曼的程序存储结构,因此运行在此平台的程序也继承了这种基因,程序设计语言如 C/Pascal 等都在一定程度上依赖于此体系。
到了 20 世纪 50 年代,一位 MIT 的教授 John McCarthy 在冯 . 诺依曼体系的机器上成功的实现了 lambda 理论,取名为 LISP(LISt Processor), 至此函数式编程语言便开始活跃于计算机科学领域。
函数式编程语言特性在函数式编程语言中,函数是第一类的对象,也就是说,函数 依赖于任何其他的对象而可以独立存在,而在面向对象的语言中,函数 ( 方法 ) 是依附于对象的,属于对象的一部分。这一点 j 决定了函数在函数式语言中的一些特别的性质,比如作为传出 / 传入参数,作为一个普通的变量等。
区别于命令式编程语言,函数式编程语言具有一些专用的概念,我们分别进行讨论:
匿名函数
在函数式编程语言中,函数是可以没有名字的,匿名函数通常表示:“可以完成某件事的一块代码”。这种表达在很多场合是有用的,因为我们有时需要用函数完成某件事,但是这个函数可能只是临时性的,那就没有理由专门为其生成一个顶层的函数对象。比如:
清单 1. map 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  function map(array, func){
  var res = [];
  for ( var i = 0, len = array.length; i < len; i++){
res.push(func(array));
     }
  return res;
}
var mapped = map([1, 3, 5, 7, 8],  function (n){
  return n = n + 1;
});

print(mapped);

运行这段代码,将会打印:

2,4,6,8,9// 对数组 [1,3,5,7,8] 中每一个元素加 1




注意 map 函数的调用,map 的第二个参数为一个函数,这个函数对 map 的第一个参数 ( 数组 ) 中的每一个都有作用,但是对于 map 之外的代码可能没有任何意义,因此,我们无需为其专门定义一个函数,匿名函数已经足够。
柯里化
柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。这句话有点绕口,我们可以通过例子来帮助理解:
清单 2. 柯里化函数
1
2
3
4
5
6
7
8
9
10
11
12
function adder(num){
  return
                 function (x){
  return num + x;
}
}

  var add5 = adder(5);
  var add6 = adder(6);

print(add5(1));
print(add6(1));




结果为:
6
7
比较有意思的是:函数 adder 接受一个参数,并返回一个函数,这个返回的函数可以被预期的那样被调用。变量 add5 保持着 adder(5) 返回的函数,这个函数可以接受一个参数,并返回参数与 5 的和。
柯里化在 DOM 的回调中非常有用,我们将在下面的小节中看到。
高阶函数
高阶函数即为对函数的进一步抽象,事实上,我们在匿名函数小节提到的 map 函数即为一种高阶函数,在很多的函数式编程语言中均有此函数。map(array, func) 的表达式已经表明,将 func 函数作用于 array 中的每一个元素,最终返回一个新的 array,应该注意的是,map 对 array 和 func 的实现是没有任何预先的假设的,因此称之为“高阶”函数:
清单 3. 高阶函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function map(array, func){
  var res = [];
  for ( var i = 0, len = array.length; i < len; i++){
         res.push(func(array));
     }
  return res;
}
var mapped = map([1, 3, 5, 7, 8],  function (n){
  return n = n + 1;
});

print(mapped);

  var mapped2 = map(["one", "two", "three", "four"],
  function (item){
  return "("+item+")";
});

print(mapped2);




将会打印如下结果:
1
2
2,4,6,8,9
(one),(two),(three),(four)// 为数组中的每个字符串加上括号




mapped 和 mapped2 均调用了 map,但是得到了截然不同的结果,因为 map 的参数本身已经进行了一次抽象,map 函数做的是第二次抽象,高阶的“阶”可以理解为抽象的层次。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0