很久之前我就学过Lisp和Erlang,但是也就是写写HelloWorld,写个排序算法。也在Coursera上听过Scala的课,可是那时候我还不怎么用Java,所以后来也没怎么继续。可是对函数式编程的兴趣一直不减,工作中几乎不会用Scala,但是用的是Java,我一直在想着怎么把Scala用到工作中。最近在写一个工具,因为这个工具基本只有我们项目组用,而且很简单,所以我就用Scala写了。以后有机会,把Scala用在生产上。
为什么对函数式编程这么感兴趣呢。
第一个原因,可以装逼,或者说是个人兴趣。函数式编程给我们提供了另一种思考问题的方法,最重要的是学习这种思维方法。
第二个原因,好多人都在学,好多地方都在用。Java 8增加了lambda表达式的支持,连C++都已经支持lambda表达式和闭包。 有越来越多的用函数式编程语言写的开源软件在被广泛使用,最有名的,用Scala写的Spark,Erlang写的RabbitMQ,当然还有用Lisp写的Emacs。函数式编程语言已经出现四十多年了,为什么现在才火起来,是因为函数式编程语言天然的适合多核编程,或者分布式编程。Google的MapReduce框架就是受到函数式编程的启发而出现的,其实Google的MapReduce和函数式编程的map reduce本质上是一样的。
函数式编程的科学解释
下面是来自知乎的一段回答,对函数式编程做了比较好的解释。
编程范式
函数式编程是一种编程范式,我们常见的编程范式有命令式编程(Imperative programming),函数式编程,逻辑式编程,常见的面向对象编程是也是一种命令式编程。
命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机的指令序列。
而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式。
函数式编程的本质
函数式编程中的函数这个术语不是指计算机中的函数(实际上是Subroutine),而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。
在函数式语言中,函数作为一等公民,可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。
纯函数式编程语言中的变量也不是命令式编程语言中的变量,即存储状态的单元,而是代数中的变量,即一个值的名称。变量的值是不可变的(immutable),也就是说不允许像命令式编程语言中那样多次给一个变量赋值。比如说在命令式编程语言我们写“x = x + 1”,这依赖可变状态的事实,拿给程序员看说是对的,但拿给数学家看,却被认为这个等式为假。
函数式语言的如条件语句,循环语句也不是命令式编程语言中的控制语句,而是函数的语法糖,比如在Scala语言中,if else不是语句而是三元运算符,是有返回值的。
严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其他命令式控制结构进行编程。
从理论上说,函数式语言也不是通过冯诺伊曼体系结构的机器上运行的,而是通过λ演算来运行的,就是通过变量替换的方式进行,变量替换为其值或表达式,函数也替换为其表达式,并根据运算符进行计算。λ演算是图灵完全(Turing completeness)的,但是大多数情况,函数式程序还是被编译成(冯诺依曼机的)机器语言的指令执行的。
一个直观的例子
在函数式编程里,function is first-class citizen。在函数式语言的代码里,你会发现各种参数都是函数,这与C语言或者JAVA完全不一样。
有这样一个例子,写一个程序对一组二维数据进行拟合。假设我有下面这些数据:
x : [1,2,3,4,5]
y: [1,4,9,16,25]
我要写一个函数,来得到这组数据的函数。注意,这时候我们要的结果不再是数据,而是函数。
假设我们要写的函数可以自己学习,每次试一个函数,不行,则继续修正(手工修正)。
C语言版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
int
main() {
int
x[5] = {1,2,3,4,5};
int
y[5] = {1,4,9,16,25};
int
a1,a2,a3;
while
(1)
{
scanf
(
"%d %d %d"
,
&a1,&a2,&a3);
try
(5,x,y,a1,a2,a3);
}
}
int
fun(
int
x,
int
a1,
int
a2,
int
a3) {
return
a1*x*x+a2*x+a3;
}
void
try
(
int
len,
int
*x,
int
*y,
int
a1,
int
a2,
int
a3) {
int
i;
for
(i = 0; i < len; i++) {
if
(y[i] != fun(x[i],a1,a2,a3) ) {
printf
(
"not match\n"
);
return
;
}
}
printf
(
"perfect match\n"
);
}
|
每次尝试后,就修改一下,最终得到一个a1*x*x+a2*x+a3的函数,实际上是一个系数。
Python版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
def
TRY(X,Y,func):
i
=
0
while
i <
len
(X):
if
func(X[i])!
=
Y[i]:
print
"not match"
return
i
=
i
+
1
print
"perfect match"
X
=
[
1
,
2
,
3
,
4
,
5
]
Y
=
[
1
,
4
,
9
,
16
,
25
]
while
1
:
func
=
raw_input
()
TRY(X,Y,
eval
(func))
appebn@EBNA3PJD:~> python find.py
lambda
x : x
*
x
+
5
not
match
lambda
x : x
*
x
*
x
*
x
*
x
*
x
+
1
not
match
lambda
x : x
*
x
perfect match
|
上面的两个程序的不同的地方在于,C语言版的函数是定死的,而Python版的函数可以作为变量输入。在C语言中,变量就是变量,函数就是函数(函数指针指针虽然可以作为参数传给另一个函数,但也是静态的,而且这里的函数更应该叫做routine)。
我们把这个例子扩展一下,假设有海量的数据,我们要用机器学习的方法得到这些数据中的一些规律,这些规律就是函数,而机器就是不断产生新的函数应用于这些数据。试想一下,在这种情况下,当然是函数式编程更理想。未来的IT系统,都是分布式的,数据都是海量的,传统的锁机制已经不能适应这种分布式编程了,而函数式编程就是一种可以在语言级解决问题的一个方法,当然不是所有问题都能解决。
函数式语言的代码更加简洁易懂
写函数式编程语言时,最难的是转变思想。例如计算一个数组的和,
C语言:
1
2
3
4
5
|
int
a[5] = {1,2,3,4,5};
int
sum = 0;
for
(
int
i = 0;i<5;i++) {
sum +=a[i];
}
|
用函数式编程语言写(Python,也可以用上面的for循环,不过那就和用C++写C语言程序一样了):
1
2
|
a
=
[
1
,
2
,
3
,
4
,
5
]
sum
=
reduce
(
lambda
x,y:x
+
y, a,
0
)
|
这里的reduce的作用是:
有一个累计值acc是0,reduce将acc=acc+y应用到每个a的元素上,最后就得到了a的各个元素的和。
Google的MapReduce就是受函数式编程语言的map和reduce启发而产生的(reduce在有的语言中叫fold,就是把列表折叠一下)。
比如我用python写一个WordCount,给一个字符串数组,返回其中某个单次的个数。
思路:找"a"的个数,将["a","b","c","a"]映射成[1,0,0,1],再求和
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#这个函数返回的是一个函数
#判断一个词是不是等于word,如果等于word,返回1,否则返回0
#在函数式编程语言中,到处倒是可以返回函数的函数
def
isTargetWord(word):
def
isWord(w):
if
w
=
=
word:
return
1
else
:
return
0
return
lambda
x: isWord(x)
words
=
[
"fuck"
,
"bitch"
,
"fuck"
]
print
"fuck:"
,
reduce
(
lambda
x,y:x
+
y,
map
(isTargetWord(
"fuck"
),words),
0
)
print
"bitch:"
,
reduce
(
lambda
x,y:x
+
y,
map
(isTargetWord(
"bitch"
),words),
0
)
|
python的lambda表达式里不支持if-else,如果用scala
1
2
|
val
words
=
List(
"fuck"
,
"bitch"
,
"fuck"
)
println(
"fuck:"
+ words.map( x
=
>
if
(x
==
"fuck"
)
1
else
0
).reduce((x,y)
=
>x+y))
|
这种语法,先map,在reduce,看起来比较自然。一但理解了函数式编程语言,那么代码将会变得特别简洁易懂。当然让一个从来没学过函数式编程的人看上面的代码,肯定一头雾水,一但懂了,就觉得写起来特别方便。
总结
总之,对于一个热爱技术的人,一定要学一下函数式编程。
本文转自nxlhero 51CTO博客,原文链接:http://blog.51cto.com/nxlhero/1661813,如需转载请自行联系原作者