小汽车前进的代码
优化为函数式的写法
有状态并不是一件很好的事情,无论是对代码重用,还是对代码的并行来说,都是有副作用的.
因此,想要个方法把这些状态搞掉,于是出现了函数式编程的编程范式.
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 '
'.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]})
上面代码呢依然把程序logic分成了函数,不过呢这些函数都是函数式的,它们有三个特点:
它们之间没有共享的变量;
函数间通过参数和返回值来传递数据;
在函数里没有临时变量.
for循环呢也被递归取代了(race函数) -- 递归就是函数式编程中常用到的技术,正如前面所说,递归的本质就是描述问题是什么.
函数式语言的三套件
函数式语言有三套件,Map啊,Reduce啊Filter啊,下面看Python的一个示例,这个示例需求为,把一个字符串数组中的字符串都转成小写.
用常规的面向过程的方式如下:
# 传统的非函数式
upname = ['G','CHEN','TANG']
lowname = []
for i in range(len(upname)):
lowname.append( upname[i].lower() )
还真是通俗易懂呢,Python也不过如此
如果写成函数式,用map()函数,是下面这个样子:
# 函数式
def toUpper(item):
return item.upper()
upper_name = map(toUpper,["r","chen","tang"])
print upper_name
# 输出 ['R','CHEN','TANG']
其中map函数第一个参数是方法名,第二个参数是方法参数
上面Python代码呢可以看到,we declare one function method called toUpper,this function has no changed the variables be put in,
只是把传进来的值做了个simple的operation,然后return,then,we using the toUpper to map function,就可以很清晰地描述出我们想要干什么,
而不是去理解一个在循环中怎样实现的代码,最终在读了很多循环的逻辑后才发现是什么意思.
如果你觉得上面的代码在传统的非函数式的方式下还是很容易读的,那么再看一个计算数组平均值的代码:
# 计算数组中正数的平均值
num = [2,-5,9,7,-2,5,3,1,0,-3,8]
positive_num_cnt = 0
positive_num_sum = 0
for i in range(len(num)):
if num[i] > 0:
positive_num_cnt += 1
positive_num_sum += num[i]
if positive_num_cnt > 0:
average = positive_num_sum / positive_num_cnt
print average
上面代码如果没注释,你需要看一会儿才能明白,只是计算数组中正数的平均值.
再看下函数式下使用 filter/reduce 函数的玩法
# 计算数组中正数的平均值
positive_num = filter(lambda x: x>0 , num)
average = reduce(lambda x,y: x+y, positive_num) / len( positive_num )
首先呢,我们使用filter函数把正数过滤出来(注意: lambda x : x>0 这个lambda表达式),
保存在一个新的数组中 -- positive_num , 然后呢,我们使用reduce函数对数组positive_num求和后,再除以个数,就得到正数的平均值了
隐藏了数组遍历并且过滤数组控制流程的filter和reduce,不仅让代码更为简洁,因为代码里只有业务逻辑了,而且让我们能更容易地理解代码.
1. 对num数组filter条件 x > 0 的数据.
2. 然后对 positive_num 进行 x + y 操作的 reduce , 即求和
3 ..
感觉代码更亲切了不是吗?
- 数据集,对数据的操作和返回值都放在了一起.
- 没有了循环体,就可以少了些临时用来控制程序执行逻辑的变量, 也少了把数据倒来倒去的控制逻辑
- **代码变成了在描述你要干什么,而不是怎么干.**