共计 2562 个字符,预计需要花费 7 分钟才能阅读完成。
概述
这篇文章里,咱们将探讨一个函数式编程的重要概念,「函数是一等公民」,顺便引出科里化、Partial Applied Function、多元函数的概念。
多元函数
第一个咱们要介绍的是多元函数的概念。其实多说无益,多元函数就是有超过一个参数的函数,它的「水管图」能够概括如下:
%%{init: {'securityLevel': 'loose', 'theme':'base'}}%%
stateDiagram-v2
parameter1--> function
parameter2 --> function
function --> [*]
这个是对水管的另一个扩大,咱们从此能够合并两个分支了。
作为参数的函数
上面,当然是咱们对一等公民的第一个表述,就是咱们能够将函数作为参数带入到另一个函数中。如下所示,此时咱们失去了水管性能的扩大。一个水管能够作为另一个水管的组成存在了。
%%{init: {'securityLevel': 'loose', 'theme':'base'}}%%
stateDiagram-v2
parameter_value --> function
state function_as_parameter {[*]}
function_as_parameter--> function
function --> [*]
这样操作最大的益处是,咱们能对函数(运算)自身进行形象解决了,譬如咱们上一章实现的 compose
命令就是一个十分典型的例子。他们是对一元函数自身的形象思考。
咱们这里举个例子,就是咱们要依照一个列表的程序来计算列表里每个二维点之间的欧式间隔,能够写成上面的函数:
import math
def euclidean_distance_between_two_ls(ls1, ls2):
euclidean_distance_between_two_point = lambda x, y: math.sqrt((x[0] - y[0]) ** 2 + (x[1] - y[1]) ** 2)
return [euclidean_distance_between_two_point(i, j) for i, j in zip(ls1, ls2)]
在实现这个例子时,咱们很快就能发现,这个函数不止能够算欧式间隔,能够形象为为所有间隔公式都能够计算的函数:
def distance_between_two_ls(ls1, ls2, distance_func):
return [distance_func(i, j) for i, j in zip(ls1, ls2)]
如果你是一个数据工作者,应该能够警惕的发现,这种思路也能够用才不便地替换「相关性」、「神经元函数」、「词干化办法」等各种畛域中。
作为返回值的函数
上面是咱们对水管的下一步扩大,也是作为函数第一等公民的下一个体现,即函数作为返回值,大抵的水管图如下:
%%{init: {'securityLevel': 'loose', 'theme':'base'}}%%
stateDiagram-v2
parameter--> function
function --> return_function
这个做的益处,咱们仍旧能够连续下面的说法,咱们对函数和运算做了更高层级的形象。当然排除掉这一点,在实用层面上说,这么做能够产生两个 Tips,提前计算和提早计算。
咱们来思考上面这个例子:
one = lambda : 1
这个例子中,咱们须要靠 one()
来失去理论的数值 1
,这个就是最小的提早计算的例子。当然,咱们在前面的例子中,将要通过这个来实现一个基于皮亚诺公理的自然数集的概念。当然更多状况里,提早计算体现在咱们只是通过一部分参数来产生一个水管而不是一个具体值,最初通过调用水管来参加计算。咱们在前一章的compose
中也体现了这个思维,咱们只是拼装水管成新水管,而不是间接调用。
当然,咱们因而也能够做到「提前计算」或者说保留状态的概念。比方上面一个例子:
def f(x, y):
z = x ** 2 + x + 1
return y * z
如果咱们常常要用到 x = 1
的状况,显然,做成返回函数的办法更无效。咱们能够将这个与面向对象的概念中如何存储更新一个对象的属性分割在一起。上面的策略实际上是函数式编程 保留状态 的最根本的办法,咱们保留了 x
等于某个值的状态(类比咱们保留了一个叫「李华」的学生的年龄,留神咱们就不须要实例化、办法、属性的概念了,咱们只有函数、函数、函数)。
def f(x):
def helper(y):
z = x ** 2 + x + 1
return y * z
return helper
这个过程咱们也叫 科里化 (Currying),然而科里化的定义十分严格,就是每次只传入一个参数,这样咱们能够始终复用x = 1
的状况了:
f1 = f(1)
# 调用
f1(2)
f1(3)
如果单纯地这么做,咱们能够用 functools.partial
来改写曾经保护好的多元函数。
from functools import partial
def f(x, y):
z = x ** 2 + x + 1
return y * z
f1 = partial(f, 1)
f1(2)
f1(3)
但作为性能调优,这种看起来函数外面套函数的写法有另外一个益处,就是后面说的「提前计算」。比方下面的例子里,显著当 x
带入后,z
是能够马上计算出来的,不须要等 y
的传入。并且,咱们发现每一次调用 f1
的时候,z
都计算了一次。于是,咱们能够看到作为返回值函数的另一个个性。上面的函数就展现了作为返回值的函数的所有技巧和益处。这种函数也被称为 Partial Applied Function 或简称为 Partial Function(留神和 001 中数学定义的偏函数的区别)。
def f(x): # x 贮存了某种咱们须要的状态
## 所有能够提前计算的放在这里
z = x ** 2 + x + 1
def helper(y):
## 所有提早计算的放在这里
return y * z
return helper
总结
作为一等公民的函数的体现:
- 咱们只有作为水管的函数和进入水管的水两个概念;
- 函数能够作为参数传入另一个函数;
- 函数能够作为另一个函数的返回值;
咱们把具备 2、3 特色中至多一个的函数称为高阶函数。高阶函数的一些劣势包含:
- 仅须要函数和值的概念,就能保留状态。而不须要更多诸如实例化、对象、可变变量、属性等概念;
- 能够提早计算,达到最根本的「惰性」的概念。
- 能够提前计算,达到更高效率地复用代码块。