函数式编程 1

小汽车前进的代码
优化为函数式的写法

有状态并不是一件很好的事情,无论是对代码重用,还是对代码的并行来说,都是有副作用的.
因此,想要个方法把这些状态搞掉,于是出现了函数式编程的编程范式.

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]})

上面代码呢依然把程序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 ..

感觉代码更亲切了不是吗?

- 数据集,对数据的操作和返回值都放在了一起.
- 没有了循环体,就可以少了些临时用来控制程序执行逻辑的变量, 也少了把数据倒来倒去的控制逻辑
- **代码变成了在描述你要干什么,而不是怎么干.**
posted @ 2020-10-05 17:55  ukyo--君君小时候  阅读(138)  评论(0编辑  收藏  举报