函数式编程语言
函数式编程语言是那些方便于使用函数式编程范式的语言。简单来说,如果具备函数式编程所需的特征, 它就可以被称为函数式语言。在多数情况下,编程的风格实际上决定了一个程序是否是函数式的。
是什么让一个语言具有函数式特征?
函数式编程无法用C语言来实现。函数式编程也无法用Java来实现(不包括那些通过大量变通手段实现的近似函数式编程)。 这些语言不包含支持函数式编程的结构。他们是纯面向对象的、严格非函数式的语言。
同时,纯函数语言也无法使用面向对象编程,比如Scheme、Haskell以及Lisp。
然而有些语言两种模式都支持。Python是个著名的例子,不过还有别的:Ruby,Julia,以及我们最感兴趣的Javascript。 这些语言是如何支持这两种差别如此之大的设计模式呢?它们包含两种编程范式所需要的特征。 然而对于Javascript来说,函数式的特征似乎是被隐藏了。
但实际上,函数式语言所需要的比上述要多一些。到底函数式语言有什么特征呢?
特点
命令式
函数式
编程风格
一步一步地执行,并且要管理状态的变化
描述问题和和所需的数据变化以解决问题
状态变化
很重要
不存在
执行顺序
很重要
不太重要
主要的控制流
循环、条件、函数调用
函数调用和递归
主要的操作单元
结构体和类对象
函数作为一等公民的对象和数据集
函数式语言的语法必须要顾及到特定的设计模式,比如类型推断系统和匿名函数。大体上,这个语言必须实现lambda演算。 并且解释器的求值策略必须是非严格、按需调用的(也叫做延迟执行),它允许不变数据结构和非严格、惰性求值。
译注:这一段用了一些函数式编程的专业词汇。lambda演算是一套函数推演的形式化系统(听起来很晕), 它的先决条件是内部函数和匿名函数。非严格求值和惰性求值差不多一个意思,就是并非严格地按照运算规则把所有元素先计算一遍, 而是根据最终的需求只计算有用的那一部分,比如我们要取有一百个元素的数组的前三项, 那惰性求值实际只会计算出一个具有三个元素是数组,而不会先去计算那个一百个元素的数组。
优点
当你最终掌握了函数式编程它将给你巨大的启迪。这样的经验会让你后面的程序员生涯更上一个台阶, 无论你是否真的会成为一个全职的函数式程序员。
不过我们现在不是在讨论如何去学习冥想;我们正在探讨如何去学习一个非常有用的工具,它将会让你成为一个更好的程序员。
总的来说,什么是使用函数式编程真正实际的优点呢?
更加简洁的代码
函数式编程更简洁、更简单、更小。它简化了调试、测试和维护。
例如,我们需要这样一个函数,它能将二维数组转化为一维数组。如果只用命令式的技术,我们会写成这样:
function merge2dArrayIntoOne(arrays) { var count = arrays.length; var merged = new Array(count); var c = 0; for (var i = 0; i < count; ++i) { for (var j = 0, jlen = arrays[i].length; j < jlen; ++j) { merged[c++] = arrays[i][j]; } } return merged }
现在使用函数式技术,可以写成这样:
merge2dArrayIntoOne2 = (arrays) -> arrays.reduce (memo, item) -> memo.concat item , []
var merge2dArrayIntoOne2 = function(arrays) { return arrays.reduce( function(p,n){ return p.concat(n); }, []); };
译注:原著中代码有误,调用reduce函数时少了第二个参数空数组,这里已经补上。
这两个函数具有同样的输入并返回相同的输出,但是函数式的例子更简洁。
模块化
函数式编程强制把大型问题拆分成解决同样问题的更小的情形,这就意味着代码会更加模块化。 模块化的程序具有更清晰的描述,更易调试,维护起来也更简单。测试也会变得更加容易, 这是由于每一个模块的代码都可以单独检测正确性。
复用性
由于其模块化的特性,函数式编程会有许多通用的辅助函数。你将会发现这里面的许多函数可以在大量不同的应用里重用。
在后面的章节里,许多最通用的函数将会被覆盖到。然而,作为一个函数式程序员,你将会不可避免地编写自己的函数库, 这些函数会被一次又一次地使用。例如一个用于在行间查找配置文件的函数,如果设计好了也可以用于查找Hash表。
减少耦合