面向数学家的函数式编程¶
本教程讨论一些函数式编程技术,数学家或使用Python进行科学计算的人可能会对这些技术感兴趣。我们首先简要概述过程性编程和面向对象编程,然后讨论函数式编程技术。在此过程中,我们将简要回顾一下对函数式编程的内置支持,包括 filter , lambda , map 和 reduce 。本教程最后提供了一些有关使用Python进行函数式编程的详细信息的参考资料。
编程风格¶
Python支持几种风格的编程。您可以通过将程序编写为指令列表来以过程化风格进行编程。假设您想在整数上实现加法和乘法。这样做的程序性程序如下:
sage: def add_ZZ(a, b):
....: return a + b
...
sage: def mult_ZZ(a, b):
....: return a * b
...
sage: add_ZZ(2, 3)
5
sage: mult_ZZ(2, 3)
6
Python型模块 operator 将几个常见的算术运算符和比较运算符定义为命名函数。加法是在内置函数中定义的 operator.add
中定义了乘法。 operator.mul
。上述示例可按如下方式完成:
sage: from operator import add
sage: from operator import mul
sage: add(2, 3)
5
sage: mul(2, 3)
6
另一种常见的编程风格称为面向对象编程。可以将对象视为同时封装数据和功能的代码。您可以在以下面向对象的实现中封装整数加法和乘法:
sage: class MyInteger:
....: def __init__(self):
....: self.cardinality = "infinite"
....: def add(self, a, b):
....: return a + b
....: def mult(self, a, b):
....: return a * b
...
sage: myZZ = MyInteger()
sage: myZZ.cardinality
'infinite'
sage: myZZ.add(2, 3)
5
sage: myZZ.mult(2, 3)
6
使用MAP进行函数式编程¶
函数式编程是将程序分解为各种功能的另一种编程风格。Python内置函数 map
, reduce
和 filter
允许您以函数式风格进行编程。请注意,在Python3中(与Python2相比),这些函数具有不同的行为,并且 reduce
已删除:如果要使用 reduce
在Python3中,您必须从导入 functools
。
功能::
map(func, seq1, seq2, ...)
接受一个函数 func
和一个或多个序列,并应用 func
这些序列中的元素。特别是,您最终得到的列表如下::
[func(seq1[0], seq2[0], ...), func(seq1[1], seq2[1], ...), ...]
在许多情况下,使用 map
允许您以简洁的方式表达程序的逻辑,而无需使用列表理解。例如,假设您有两个整数列表,并且希望按元素将它们相加。实现这一目标的清单理解如下:
sage: A = [1, 2, 3, 4]
sage: B = [2, 3, 5, 7]
sage: [A[i] + B[i] for i in range(len(A))]
[3, 5, 8, 11]
或者,您也可以使用Python内置加法函数 operator.add
与.一起 map
要达到同样的效果::
sage: from operator import add
sage: A = [1, 2, 3, 4]
sage: B = [2, 3, 5, 7]
sage: list(map(add, A, B))
[3, 5, 8, 11]
的优势 map
您不需要像在上面的列表理解中那样显式定义for循环。
使用lambda定义小函数¶
有时,您想要编写一个简短的单行函数。您可以重写上述加法函数,如下所示:
sage: def add_ZZ(a, b): return a + b
...
或者,您可以使用 lambda
语句以更清晰的方式做同样的事情。上面的加法和乘法函数可以使用 lambda
详情如下:
sage: add_ZZ = lambda a, b: a + b
sage: mult_ZZ = lambda a, b: a * b
sage: add_ZZ(2, 3)
5
sage: mult_ZZ(2, 3)
6
一旦你们结合在一起,事情就会变得更有趣 map
与 lambda
陈述。作为练习,您可以尝试编写一个简单的函数,该函数实现 Chinese Remainder Theorem 。您可以将列表理解与以下内容一起使用 map
和 lambda
如下所示。在这里,参数 A
是一个整数列表, M
是模数的列表。**
sage: def crt(A, M):
....: Mprod = prod(M)
....: Mdiv = list(map(lambda x: Integer(Mprod / x), M))
....: X = list(map(inverse_mod, Mdiv, M))
....: x = sum([A[i]*X[i]*Mdiv[i] for i in range(len(A))])
....: return mod(x, Mprod).lift()
...
sage: A = [2, 3, 1]
sage: M = [3, 4, 5]
sage: x = crt(A, M); x
11
sage: mod(x, 3)
2
sage: mod(x, 4)
3
sage: mod(x, 5)
1
为了在环上产生一个随机矩阵,比如说 ZZ ,您可以从定义一个矩阵空间开始,然后获得该矩阵空间的随机元素:
sage: MS = MatrixSpace(ZZ, nrows=5, ncols=3)
sage: MS.random_element() # random
<BLANKLINE>
[ 6 1 0]
[-1 5 0]
[-1 0 0]
[-5 0 1]
[ 1 -1 -3]
或者,您可以使用函数 random_matrix
**
sage: random_matrix(ZZ, nrows=5, ncols=3) # random
<BLANKLINE>
[ 2 -50 0]
[ -1 0 -6]
[ -4 -1 -1]
[ 1 1 3]
[ 2 -1 -1]
下一个示例使用 map
要构造随机整数矩阵列表,请执行以下操作:
sage: rows = [randint(1, 10) for i in range(10)]
sage: cols = [randint(1, 10) for i in range(10)]
sage: rings = [ZZ]*10
sage: M = list(map(random_matrix, rings, rows, cols))
sage: M[0] # random
<BLANKLINE>
[ -1 -3 -1 -37 1 -1 -4 5]
[ 2 1 1 5 2 1 -2 1]
[ -1 0 -4 0 -2 1 -2 1]
如果您希望对矩阵的条目进行更多控制,而不是 random_matrix
功能许可,您可以使用 lambda
与.一起 map
详情如下:
sage: rand_row = lambda n: [randint(1, 10) for i in range(n)]
sage: rand_mat = lambda nrows, ncols: [rand_row(ncols) for i in range(nrows)]
sage: matrix(rand_mat(5, 3)) # random
<BLANKLINE>
[ 2 9 10]
[ 8 8 9]
[ 6 7 6]
[ 9 2 10]
[ 2 6 2]
sage: rows = [randint(1, 10) for i in range(10)]
sage: cols = [randint(1, 10) for i in range(10)]
sage: M = list(map(rand_mat, rows, cols))
sage: M = list(map(matrix, M))
sage: M[0] # random
<BLANKLINE>
[ 9 1 5 2 10 10 1]
[ 3 4 3 7 4 3 7]
[ 4 8 7 6 4 2 10]
[ 1 6 3 3 6 2 1]
[ 5 5 2 6 4 3 4]
[ 6 6 2 9 4 5 1]
[10 2 5 5 7 10 4]
[ 2 7 3 5 10 8 1]
[ 1 5 1 7 8 8 6]
将序列简化为一个值¶
该功能 reduce
获取两个参数的函数,并将其应用于给定的序列,以将该序列减少为单个值。该功能 sum 是一个示例,它是 reduce
功能。下面的示例代码使用 reduce
和内置函数 operator.add
将给定列表中的所有整数相加。这之后是使用 sum
要完成相同的任务::
sage: from functools import reduce
sage: from operator import add
sage: L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sage: reduce(add, L)
55
sage: sum(L)
55
在下面的示例代码中,我们将向量视为实数列表。这个 dot product 然后使用以下函数实现 operator.add
和 operator.mul
,与内置的Python函数结合使用 reduce
和 map
。然后,我们将展示如何 sum
和 map
可以组合在一起产生相同的结果。**
sage: from functools import reduce
sage: from operator import add
sage: from operator import mul
sage: U = [1, 2, 3]
sage: V = [2, 3, 5]
sage: reduce(add, map(mul, U, V))
23
sage: sum(map(mul, U, V))
23
或者,您可以使用Sage对点产品的内置支持::
sage: u = vector([1, 2, 3])
sage: v = vector([2, 3, 5])
sage: u.dot_product(v)
23
下面是中国剩余定理的一个实现,不使用 sum
和以前一样。下面的版本使用 operator.add
并定义了 mul3
乘三个数而不是两个。**
sage: from functools import reduce
sage: def crt(A, M):
....: from operator import add
....: Mprod = prod(M)
....: Mdiv = list(map(lambda x: Integer(Mprod / x), M))
....: X = map(inverse_mod, Mdiv, M)
....: mul3 = lambda a, b, c: a * b * c
....: x = reduce(add, map(mul3, A, X, Mdiv))
....: return mod(x, Mprod).lift()
...
sage: A = [2, 3, 1]
sage: M = [3, 4, 5]
sage: x = crt(A, M); x
11
使用过滤器进行过滤¶
Python内置函数 filter
接受一个参数和一个序列的函数。然后,它返回给定序列中所有这些项的列表,以便新列表中的任何项都会导致给定函数返回 True
。从某种意义上说,您过滤掉了满足给定函数中定义的某个条件(S)的所有项。例如,您可以使用 filter
过滤掉1到50之间(包括1和50)的所有素数。**
sage: list(filter(is_prime, [1..50]))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
对于给定的正整数 n, the Euler phi function 计算整数的个数 a 同 1 leq a leq n ,以便 gcd(a, n) = 1 。您可以使用列表理解来获得所有这些信息 a 是什么时候 n = 20 **
sage: [k for k in range(1, 21) if gcd(k, 20) == 1]
[1, 3, 7, 9, 11, 13, 17, 19]
一种功能方法是使用 lambda
定义一个函数来确定给定的整数是否相对质数为20。 filter
而不是列表理解,以获得所有必需的 a 的.::
sage: is_coprime = lambda k: gcd(k, 20) == 1
sage: list(filter(is_coprime, range(1, 21)))
[1, 3, 7, 9, 11, 13, 17, 19]
该功能 primroots
下面定义的返回以给定的正素数为模的所有原根 p 。它使用 filter
获取之间的整数列表 1 和 p - 1 ,包括在内,列表中的每个整数相对于乘法群的顺序是相对质数的 (ZZ/pZZ)^{ast} 。**
sage: def primroots(p):
....: g = primitive_root(p)
....: znorder = p - 1
....: is_coprime = lambda x: gcd(x, znorder) == 1
....: good_odd_integers = filter(is_coprime, [1..p-1, step=2])
....: all_primroots = [power_mod(g, k, p) for k in good_odd_integers]
....: all_primroots.sort()
....: return all_primroots
...
sage: primroots(3)
[2]
sage: primroots(5)
[2, 3]
sage: primroots(7)
[3, 5]
sage: primroots(11)
[2, 6, 7, 8]
sage: primroots(13)
[2, 6, 7, 11]
sage: primroots(17)
[3, 5, 6, 7, 10, 11, 12, 14]
sage: primroots(23)
[5, 7, 10, 11, 14, 15, 17, 19, 20, 21]
sage: primroots(29)
[2, 3, 8, 10, 11, 14, 15, 18, 19, 21, 26, 27]
sage: primroots(31)
[3, 11, 12, 13, 17, 21, 22, 24]
进一步的资源¶
这是一篇相当简短的关于使用Python进行函数式编程的教程。Python标准文档有一个内置函数列表,其中许多函数在函数式编程中很有用。例如,您可能想要阅读有关 all , any , max , min ,以及 zip 。Python型模块 operator 有许多内置的算术运算符和比较运算符,每个运算符都实现为一个函数,其名称反映了其预期用途。对于算术和比较运算,建议您参考 operator
模块来确定是否有满足您需求的内置函数。该模块 itertools 具有许多内置功能,可以有效地处理项目序列。
使用Python进行函数式编程的另一个有用资源是 Functional Programming HOWTO 作者:A.M.库奇林。史蒂文·F·洛特的书 Building Skills in Python 有一章是关于 Functional Programming using Collections 。另请参阅本章 Functional Programming 出自马克·皮尔格林的书 Dive Into Python 。
您可能还想考虑尝试使用 Haskell 用来表达数学概念。有关Haskell表达数学算法的示例,请参阅J.Gibbons的文章 Unbounded Spigot Algorithms for the Digits of Pi 发表在《美国数学月刊》上。