Python 函数编程

1. 乱谈函数编程

  在大学里,老师教面向对象编程和面向过程编程的数不胜数,而函数编程却是少之又少。

  其实函数编程的概念出现的比面向对象要早得多,按照百度百科的解释:函数式编程是种编程典范,它将电脑运算视为函数的计算。

  两年前,我第一次听说函数编程的概念,记得当时的函数编程介绍会议上我被忽悠的一愣一愣的,当场下定决心学函数编程,这么高端的东西我都没有用过,简直羞于提起我是IT人员啊!当然,迫于各种现实问题,当时我还是没有去学习使用函数编程,好吧,羞愧一下····

  近段时间开始学习python,发现python对于函数编程也有支持,于是乎理所当然的要捣鼓一下。

  先讲讲我理解的函数编程(以下纯属个人理解,如果有错误,纯属正常) :

  首先,函数编程必须做到的一点,函数的参数可以是函数,函数的返回值也可以是函数。(如果这都不能做到,还叫啥函数编程?)

  其次,可以使用函数过程代替条件以及循环语句,条件语句很容易代替,部分循环语句就需要使用递归了。

  最后,函数编程不能赋值。函数编程中最好没有变量,就算有也只是一个名称,而不是一个存储单元。

2. Python对函数编程的支持

  好么,乱七八糟说了一通以后,回到正题,Python中是如何支持函数编程的呢?

  按照在第一节中说的对于函数编程的理解来看,第一点,Python本身及支持函数的传递。第二点,Python中有以下几个函数:lambda、map、reduce、filter,加上Python对于条件判断的短路法则,完全可以做到。最后一点,则需要编码者自己约束与规范了。

  2.1 函数传递

  下面的代码使用了Python的函数传递(忘了交代了,我是用的是python 2.7):

 1 def my_filter(list,rule_func):
 2     result = []
 3     for item in list:
 4         if rule_func(item):
 5             result.append(item)
 6     return result
 7 
 8 def rule_positive(num):
 9     if num>0:
10         return True
11     return False
12 
13 def rule_len_noless_three(str):
14     if len(str)>=3:
15         return True
16     return False
17 
18 if __name__ == '__main__':
19     list_int = [1,-8,2,6,-95,-88]
20     list_str = 'My name is Python'.split()
21     print (my_filter(list_int,rule_positive))
22     print (my_filter(list_str,rule_len_noless_three))

  这个代码试图完成一个过滤器,my_filter()函数的参数是一个列表与一个规则函数,my_filter()函数使用规则函数对列表中的元素进行筛选。通过例子,可以发现其实python对待函数就和面向对象编程语言中对待对象是一样的,直接把规则函数名当做对象传入my_filter()函数,在my_filter()函数中就可以直接使用规则函数了。就这样,python轻松的满足了函数编程的第一个要求。

  2.2 lambda函数

  百度百科在介绍函数编程的时候强调:函数编程语言最重要的基础是 λ 演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。

  既然python可以支持函数编程,理所当然的,python有一个lambda演算。

  以2.1中的规则函数为例,将rule_positive()以及rule_len_noless_three()函数修改为同样功能的lambda演算:

1 rule_positive = lambda x:x > 0
2 rule_len_noless_three = lambda x:len(x) >= 3

  更进一步,将my_filter()函数中的判断功能也用lambda演算代替(有人会认为多此一举,其实这里这么做的主要原因是可以清楚的看到lambda演算可以接受函数作为输入,当处理到一些很复杂的运算或数学推理时,用到这一点可能就会带来很大的方便):

1 judge_func = lambda n,f:f(n)
2 judge_func(-1,rule_positive)

  从代码内容就可以直观的看出来这个lambda演算打算干什么,传入了一个值n和一个函数f,使用函数f对值n进行操作。

  2.3 使用map、reduce、filter函数代替循环

  在使用函数编程时,经常会需要一些短小精炼的函数去组织一些元素,就像人类语言中的谓词。一般面向对象编程时使用的for语句块、while循环以及if-else语句块都是属于用户自定义的“谓词”。值得庆幸的是,在Python中,已经提供了map(), reduce()以及filter()三个内嵌函数。

  下面我们来看看如何使用这三个函数来代替循环语句块:

  map()函数顾名思义是用来映射的,它使用传入的函数对列表的每个值进行操作,最后返回一个列表。

#不使用map()函数求平方
1
list = [1, 2, 3, 4] 2 result = [] 3 for item in list: 4 result.append(item * item) 5 print(result)
#使用map()函数
1
list = [1, 2, 3, 4] 2 print(map(lambda x:x*x, list))

  上面两个代码片段做的是同样的事情,对于list中的每个值进行平方操作。这里的lambda演算直接写在map()函数中作为一个参数,这样做首先符合了我理解的函数编程的第三点——不赋值,还使得代码的可读性更高,这个map()到底做了什么一幕了然。

  reduce()函数的中文解释是“减少”,它的作用是使用传入的函数对列表从头到尾进行一一操作,每次操作有两个数,操作的结果作为下一次操作的其中一个数,一直到列表结束,得到最后一个结果。也就是说在这里传入的函数必须是对两个数进行操作的函数。举个最简单易懂的例子,求10的阶层:

#不使用reduce()函数求10的阶层
1
result = 1 2 for num in range(1, 11): 3 result = result * num 4 print(result)
#使用reduce()函数
1
print(reduce(lambda x, y:x * y, range(1, 11)))

  filter()函数就是用来过滤的,所以其实2.1中想做的过滤函数python已经有内嵌函数完成了,它使用一个返回bool值得函数来对列表中的值进行判断,留下通过判断的值,忽略通不过的,将2.1中的过滤函数使用filter()函数代替:

1 rule_positive = lambda x:x > 0
2 rule_len_noless_three = lambda x:len(x) >= 3
3 
4 if __name__ == '__main__':
5     list_int = [1, -8, 2, 6, -95, -88]
6     list_str = 'My name is Python'.split()
7     print (filter(rule_positive, list_int))
8     print (filter(rule_len_noless_three, list_str))

  2.4 不赋值(避免副作用)

  我看了很多资料,提到函数编程优点的时候大多都会提到“避免副作用”,这是一个很抽象的词语,我更喜欢直接把这个优点叫做“不赋值不出错”,直观易懂。如何理解“不赋值不出错”?

1 list = [3,5,8,23]
2 result = []
3 for num in list:
4     tmp_num = num+1
5     #对tmp_num做一些别的操作
6     if tmp_num%2 == 0:
7         result.append(num)
8 print(result)

  上面这段代码的主要功能是获得list中的奇数,但是就像我在代码中注释的,如果我想获取的不是奇数,而是对原始数据经过一系列操作后可以成为偶数的数,那么肯定需要更多对于变量tmp_num的操作,而这些操作就很有可能导致结果出错,而且tmp_num这个变量是零时性的,后继代码很可能根本需要知道它是什么。所以赋值就代表容易出现错误,这就是“赋值操作的副作用”。当然,避免这种“赋值副作用”的方法很简单,就是“不赋值”。

  从多线程并行开发也很容易理解这一点。并行开发经常出现的同步问题,就是因为在并行语句块中存在着赋值语句,如果这些赋值语句不存在,同步的问题就根本不需要考虑。

  既然写到这里,我们顺便把2.1的代码块变成没有赋值语句的代码吧:

1 if __name__ == '__main__':
2     print (filter(lambda x:x > 0, [1, -8, 2, 6, -95, -88]))
3     print (filter(lambda x:len(x) >= 3, 'My name is Python'.split()))

  2.5 Python列表内嵌循环

  其实这一点不应该是函数编程的,是python的一个特性,但是我觉得挺实用也挺有意思的。修改后的2.1代码:

1 if __name__ == '__main__':
2     list_int = [1, -8, 2, 6, -95, -88]
3     list_str = 'My name is Python'.split()
4     print ([x for x in list_int if x > 0])
5     print ([x for x in list_str if len(x) >= 3])

  对于那些本身使用过面向对象或者面向过程编程的人来说(比如我),这个看起来更直观,并且更愿意使用。

3. 结个尾

  以上所诉,我自己看看都讲得乱起八糟,没有连贯性,但是已经是我的极其努力的结果了呀,哎╮(╯▽╰)╭

  总的而言,python对函数编程的支持还是挺充分的,而且根据函数编程的特性来看,函数编程比较善于应用在多线程编程以及数学运算等方面。

  

posted @ 2013-06-20 01:47  略二真人  阅读(292)  评论(0编辑  收藏  举报