函数式编程的概念
函数式编程理念来自于数学中的函数。函数的概念:对于两个变量x和y,如果每给定x的一个值,都有一个y值与之对应,那么我们就说y是x的函数。如下即是一些函数:
f(x)=5x^2+4x+3
g(x)=2f(x)+5=10x^2+8x+11
h(x)=f(x)+g(x)=15x^2+12x+14
这种只关心输入数据和输出数据的关系,即数学表达式里面说的mapping关系的编程模式叫做函数式编程。对于函数式编程来说,x是输入数据,y是输出数据。
函数式编程的特点:
stateless:函数不维护任何外部或者内部状态,也不依赖任何内部或者外部状态。只做一件事:你给我输入数据,然后我给你输出数据。
immutable:输入数据不变。输入数据变了,相当于函数之间就是有状态了(隐式地),调用顺序一变,整个结果就有可能变化。
函数式编程的优势:
没有状态就没有伤害:无状态的代码功能性
并行执行无伤害:因为无状态,线程安全,可并行执行
Copy-Paste 重构代码无伤害
函数的执行没有顺序上的问题
函数式编程的劣势:
数据拷贝比较严重。因为无状态,所以数据都是拷贝的。但是这并不代表性能会很差,因为无状态,所以多线程不需要加锁,性能反而可能更好。
函数式编程的思维方式:
函数式编程关注的是:describe what to do , rather than how to do it.于是, 我们把以前过程式的编程范式叫做Imperative Programming(指令式编程). 而把函数式编程编程范式叫做Declarative Programming(声明式编程)。 一个具体例子:
我们有 3 辆车比赛,简单起见,我们分别给这 3 辆车 70% 的概率让它们可以往前走一步,一共有 5 次机会,然后打出每一次这 3 辆车的前行状态。
指令式编程的写法(python):
from random import random
def move_cars():
for i, _ in enumerate(car_positions):
if random() > 0.3:
car_positions[i] += 1
def draw_car(car_position):
print '-' * car_position
def run_step_of_race():
global time
time -= 1
move_cars()
def draw():
print ''
for car_position in car_positions:
draw_car(car_position)
time = 5
car_positions = [1, 1, 1]
while time:
run_step_of_race()
draw()
上面的代码,从主循环开始,我们可以很清楚地看到程序的主干,因为我们把程序的逻辑分成了几个函数。这样一来,代码逻辑就会变成几个小碎片,于是我们读代码时要考虑的上下文就少了很多,阅读代码也会更容易。不像第一个示例,如果没有注释和说明,你还是需要花些时间理解一下。而将代码逻辑封装成了函数后,我们就相当于给每个相对独立的程序逻辑取了个名字,于是代码成了自解释的。但是,你会发现,封装成函数后,这些函数都会依赖于共享的变量来同步其状态。于是,在读代码的过程中,每当我们进入到函数里,读到访问了一个外部的变量时,我们马上要去查看这个变量的上下文,然后还要在大脑里推演这个变量的状态, 才能知道程序的真正逻辑。也就是说,这些函数必须知道其它函数是怎么修改它们之间的共享变量的,所以,这些函数是有状态的。
我们知道,有状态并不是一件很好的事情,无论是对代码重用,还是对代码的并行来说,都是有副作用的。因此,要想个方法把这些状态搞掉,于是出现了函数式编程的编程范式。下面,我们来看看函数式的方式应该怎么写?
函数式编程的写法:
from random import random
def move_cars(car_positions):
return map(lambda x: x + 1 if random() > 0.3 else x,
car_positions)
def output_car(car_position):
return '-' * car_position
def run_step_of_race(state):
return {'time': state['time'] - 1,
'car_positions': move_cars(state['car_positions'])}
def draw(state):
print ''
print '\n'.join(map(output_car, state['car_positions']))
def race(state):
draw(state)
if state['time']:
race(run_step_of_race(state))
race({'time': 5,
'car_positions': [1, 1, 1]})
上面的代码依然把程序的逻辑分成了函数。不过这些函数都是函数式的,它们有三个特点:它们之间没有共享的变量;函数间通过参数和返回值来传递数据;在函数里没有临时变量。我们还可以看到,for 循环被递归取代了(见 race 函数)—— 递归是函数式编程中常用到的技术,正如前面所说的,递归的本质就是描述问题是什么