数学家函数编程

本教程讨论了一些函数编程技术,这些技术可能会引起数学家或使用Python进行科学计算的人的兴趣。我们从过程式和面向对象编程的简要概述开始,然后讨论函数式编程技术。在此过程中,我们简要回顾Python对函数式编程的内置支持,包括 filterlambdamapreduce . 本教程最后提供了一些有关使用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内置函数 mapreducefilter 允许您使用函数式编程。请注意,在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.addmap 为了达到同样的效果:

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

一旦你结合在一起,事情就变得更有趣了 maplambda 声明。作为练习,您可以尝试编写一个简单的函数来实现 Chinese Remainder Theorem . 你可以把列表理解和 maplambda 如下所示。这里,参数 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 功能允许,你可以使用 lambdamap 如下:

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 # py3
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.addoperator.mul ,以及内置的Python函数 reducemap . 然后我们展示如何 summap 可以组合起来产生同样的结果。:

sage: from functools import reduce # py3
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对dot产品的内置支持:

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 # py3
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 . 从某种意义上说,您过滤掉了满足给定函数中定义的某些条件的所有项。例如,您可以使用 filter 过滤掉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 计算整数的数目 a1 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 s.::

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 获取 1p - 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标准文档有一个内置函数的列表,其中许多函数在函数编程中很有用。例如,您可能需要阅读 allanymaxminzip . Python模块 operator 有许多内置的算术和比较运算符,每个运算符都被实现为一个函数,其名称反映了其预期用途。对于算术和比较操作,建议您参考 operator 模块来确定是否有满足您需求的内置函数。模块 itertools 有许多内置函数,以有效地处理项目序列。

Python中函数式编程的另一个有用资源是 Functional Programming HOWTO 作者:A.M.Kuchling。史蒂芬·洛特的书 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 在美国数学月刊上。