赞
踩
编程范式(Programming Paradigm)是某类典型编程风格或者说是编程方式。根据不同的分类方式,可以对编程范式进行不同形式的划分。常见的有以下两种划分结果:
说明:
注意:
程序的状态:包含了当前定义的全部变量,即程序定义的全部变量来表示当前状态。程序调试过程中的每一步就是保存了程序的当前状态。有了程序的状态,我们的程序才能不断往前推进。前进的过程就是状态改变的过程。
命令式编程:通过不断修改变量的值,保存当前运行的状态来步步推进。总结就是命令式编程的代码由一系列改变全局状态的语句构成。
命令式编程的运作方式具有图灵机特性,且和冯诺依曼体系的对应关系非常密切。甚至可以说,命令式程序就是一个冯诺依曼机的指令序列:
此外,命令式程序执行的效率取决于执行命令的数量,因此才会出现大O表示法等等表示时间空间复杂度的符号。
函数式编程中的函数,这个术语不是指命令式编程中的函数(可以认为C++程序中的函数本质是一段子程序Subroutine),而是指数学中的函数,即自变量的映射(一种东西和另一种东西之间的对应关系)。也就是说,一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。
在函数式语言中,函数作为一等公民,可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。
纯函数式编程语言中的变量也不是命令式编程语言中的变量,即存储状态的单元,而是代数中的变量,即一个值的名称。变量的值是不可变的(immutable),也就是说不允许像命令式编程语言中那样多次给一个变量赋值。比如说在命令式编程语言我们写“x = x + 1”,这依赖可变状态的事实,拿给程序员看说是对的,但拿给数学家看,却被认为这个等式为假。
函数式语言的如条件语句,循环语句也不是命令式编程语言中的控制语句,而是函数的语法糖,比如在Scala语言中,if else不是语句而是三元运算符,是有返回值的。
严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其他命令式控制结构进行编程。
函数式的最主要的好处主要是不可变性带来的。没有可变的状态,函数就是引用透明(Referential transparency)的和没有副作用(No Side Effect)。
引用透明,即函数运行的结果只依赖于输入的参数,而不依赖于外部状态
No Side Effect,即函数的所有功能就仅仅是返回一个新的值而已,没有其他行为,尤其是不得修改外部变量,因而各个独立的部分的执行顺序可以随意打乱,带来执行顺序上的自由,也因此使得一系列新的特性得以实现:无锁的并发;惰性求值;编译器级别的性能优化等
注意:由于变量不可变,纯函数编程语言无法实现循环,这是因为For循环使用可变的状态作为计数器,而While循环或DoWhile循环需要可变的状态作为跳出循环的条件。因此在函数式语言里就只能使用递归来解决迭代问题,这使得函数式编程严重依赖递归。
Python中的函数式编程实现:
总结:
Python中的一等对象(first-class object)定义:
Python中,所有函数都是一等对象
定义:接受函数为参数,或把函数作为返回结果的函数
下面介绍一些常用的高阶函数。
map(function, iterable, ...)
>>> def square(x): return x * x
...
>>> xx = map(square, range(10))
>>> xx
<map object at 0x7f993713bef0>
>>> xx = list(xx)
>>> xx
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
上述代码可用[x * x for x in range(10)]
代替。实际上,map可以使用列表解析式来代替。
filter(function, iterable)
>>> x = [(), [], {}, None, '', False, 0, True, 1, 2, -3]
>>> x_result = filter(bool, x)
>>> x_result
<filter object at 0x7f993713beb8>
>>> x_result = list(x_result)
>>> x_result
[True, 1, 2, -3]
上述代码可用[i for i in x if bool(i)]
代替。实际上,和map相同,filter也可以使用列表解析式来代替。
因此,对序列中的每一个元素执行某一行为的时候均可以用列表解析式来代替。
reduce(function, iterable[, initializer])
>>> def multiply(a,b): return a * b
...
>>> from functools import reduce
>>> reduce(multiply, range(1,5))
24
reduce函数很难用列表解析式来代替,因为其执行的行为需要同时对序列中的两个元素进行处理。
sorted(iterable[, cmp[, key[, reverse]]])
参数说明:
- iterable – 可迭代对象。
- cmp – 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
- key – 用列表元素的某个属性或传入函数进行作为关键字。接受的函数只能有一个参数,且其返回值表示此元素的权值,sorted将按照权值大小进行排序。
- reverse – 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
>>> sorted([x * (-1) ** x for x in range(10)])
[-9, -7, -5, -3, -1, 0, 2, 4, 6, 8]
>>> sorted([x * (-1) ** x for x in range(10)], reverse=True)
[8, 6, 4, 2, 0, -1, -3, -5, -7, -9]
>>> sorted([x * (-1) ** x for x in range(10)], key=abs)
[0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
>>> sorted([x * (-1) ** x for x in range(10)], reverse=True, key=abs)
[-9, 8, -7, 6, -5, 4, -3, 2, -1, 0]
max/min也有类似的功能,其语法为max/min(iterable, key, default)
。执行过程为:遍历一遍这个迭代器,然后将迭代器的每一个返回值当做参数传给key=func 中的func(一般用lambda表达式定义) ,然后将func的执行结果传给key,然后以key为标准进行大小的判断。
实际上,Python中有很多类似执行过程的函数,其参数列表中都有key参数。
partial(func, *args, **keywords)
参数说明:
- func: 需要被扩展的函数,返回的函数其实是一个类 func 的函数
- *args: 需要被固定的位置参数
- **kwargs: 需要被固定的关键字参数
如果在原来的函数 func 中关键字不存在,将会扩展,如果存在,则会覆盖
>>> from functools import partial
>>> abs_sorted = partial(sorted, key=abs)
>>> abs_sorted([x * (-1) ** x for x in range(10)])
[0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
>>> abs_reverse_sorted = partial(sorted, key=abs, reverse=True)
>>> abs_reverse_sorted([x * (-1) ** x for x in range(10)])
[-9, 8, -7, 6, -5, 4, -3, 2, -1, 0]
>>> from functools import partial >>> def add(*args, **kwargs): ... # 打印位置参数 ... for n in args: ... print(n) ... print("-"*20) ... # 打印关键字参数 ... for k, v in kwargs.items(): ... print('%s:%s' % (k, v)) ... # 暂不做返回,只看下参数效果,理解 partial 用法 ... >>> add_partial = partial(add, 10, k1=10, k2=20) >>> add_partial(1, 2, 3, k3=20) 10 1 2 3 -------------------- k1:10 k2:20 k3:20 >>> add_partial(1, 2, 3, k1=20) #会覆盖已固定的关键字参数 10 1 2 3 -------------------- k1:20 k2:20
定义:使用lambda表达式创建的函数,函数本身没有名字。通常用于需要一个函数,但是不想去命名一个新函数的情况下。
Expressions get a value; Statements do something.
两种的区别就是表达式可以求值,但是语句不可以。
因此,表达式可以出现在“值”出现的地方,如:
result = add( x + 1, y)
而把一个语句作为参数则不可以
result = add( if x == 1:pass , y)
关系:
Compound Statements > Simple Statements > Expressions
复合语句由多个单个语句组成,而单个语句可以是一个表达式也可以不是表达式。
与def
的区别:
Tips:
>>> def multiply(a,b): return a * b
...
>>> multiply(3,4)
12
>>> multiply_by_lambda = lambda x,y: x * y #此处将lambda表达式进行赋值仅用来讲解,实际中常与高阶函数结合,作为函数参数传入
>>> multiply_by_lambda(3,4)
12
>>> f_list = [lambda x: x + 1, lambda x: x ** 2, lambda x: x ** 3]
>>> [f_list[j](10) for j in range(3)]
[11, 100, 1000]
在AI领域里,这种写法常用于处理数据,比如按预定的一系列模式处理数据
>>> f_list = [lambda x: x**i for i in range(5)] #可理解为[(lambda x: x**i) for i in range(5)]
>>> [f_list[j](10) for j in range(5)]
上述代码输出结果为:
[10000, 10000, 10000, 10000, 10000]
这和python的惰性求值有关。惰性求值,也就是延迟求值,表达式不会在它被绑定到变量之后就立即求值,而是等用到时再求值。
此例中只有当lambda表达式被调用时才将i的值传给它,也就是循环的终值4,所以调用结果均为10000。
因为迭代器中所有的数据都被放在内存中以供使用,所以会对内存造成很大的压力。例如:
>>> for num in range(10**1000):
string = ' ' + ' '
....
OverflowError: range() result has too many items
生成器具备迭代器的所有特性,但是它仅供迭代一次,因为它采用的惰性计算的优化策略,就是说它只有当被使用到的时候才把数据取出或者计算出,放到内存中,访问之后随机销毁。
在使用上,它和迭代器的主要区别在于仅能进行一次迭代访问。
>>> iterator = [x for x in range(7)]
>>> Iterator
[0, 1, 2, 3, 4, 5, 6]
>>> generator = (x for x in range(7))
( generator object (genexpr) at 0X00000000025DBA20)
惰性求值不仅在内存层级对空间有着优化的效果,在计算时间上也有着一定的提高。由于避免了不必要的计算,节省了计算资源。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。