当前位置: 首页 > 后端技术 > Python

Python函数式编程系列003:一等公民

时间:2023-03-26 12:06:20 Python

概述本篇我们将讨论函数式编程的一个重要概念“函数是一等公民”,顺便介绍一下柯里化、PartialAppliedFunction、和多元函数的概念。多元函数我们首先要介绍的是多元函数的概念。其实说多了也没用。多元函数是具有多个参数的函数。它的“水管图”可以概括如下:%%{init:{'securityLevel':'loose','theme':'base'}}%%stateDiagram-v2parameter1-->functionparameter2-->functionfunction-->[*]这是水管的另一个扩展,我们可以从现在开始合并这两个分支。下面的函数作为参数,当然是我们一等公民的第一个声明,就是我们可以把函数作为参数传入其他函数。如下所示,此时我们得到了管道功能的扩展。管道现在可以作为另一个管道的组件存在。%%{init:{'securityLevel':'loose','theme':'base'}}%%stateDiagram-v2parameter_value-->functionstatefunction_as_parameter{[*]}function_as_parameter-->function函数-->[*]这个操作最大的好处就是我们可以抽象出函数(操作)本身。比如上一章我们实现的compose命令就是一个很典型的例子。它们是对一元函数本身的抽象思考。这里举个例子,就是我们要按照列表的顺序计算列表中每个二维点之间的欧氏距离,可以写成如下函数:importmathdefeuclidean_distance_between_two_ls(ls1,ls2):euclidean_distance_between_two_point=lambdax,y:math.sqrt((x[0]-y[0])**2+(x[1]-y[1])**2)返回[euclidean_distance_between_two_point(i,j)fori,jinzip(ls1,ls2)]完成这个例子,我们很快就会发现这个函数不仅可以计算欧式距离,还可以抽象为一个可以计算所有距离公式的函数:defdistance_between_two_ls(ls1,ls2,distance_func):return[distance_func(i,j)fori,jinzip(ls1,ls2)]如果你是数据工作者,应该能警惕地发现,这种思路也可以用轻松替代“相关性”、“神经元功能”、“词干提取方法”等领域。函数作为返回值是我们对水管的下一个扩展,也是函数一等公民的下一个体现,即函数作为返回值。大概的水管图如下:%%{init:{'securityLevel':'loose','theme':'base'}}%%stateDiagram-v2parameter-->function函数-->return_function,我们可以还是延续上面的说法,我们做了更高层次的功能和操作抽象。当然,这个除外。在实际层面上,这样做可以产生两个Tips,提前计算和延迟计算。让我们考虑以下示例:one=lambda:1在这个示例中,我们需要依赖one()来获取实际值1,这是最小延迟计算的示例。当然,在后面的例子中,我们将以此来实现基于皮亚诺公理的自然数集的概念。当然,更多情况下,延迟计算体现在我们只是通过一些参数而不是具体的值来生成水管,最后通过调用水管来参与计算。我们在上一章的compose中也体现了这种思想。我们只是把水管组装成新的水管,而不是直接调用。当然,我们也可以实现“提前计算”或保存状态的概念。比如下面这个例子:deff(x,y):z=x**2+x+1returny*z如果我们经常需要用到x=1的情况,显然,return的方法功能更有效。我们可以将其与如何存储和更新对象属性的面向对象概念联系起来。下面的策略其实是函数式编程中最基本的状态保存方式。我们保存x等于某个值的状态(类比我们保存一个叫“李华”的学生的年龄,注意我们不需要实例化,方法,属性概念,我们只有function,function,function).deff(x):defhelper(y):z=x**2+x+1returny*zreturnhelper这个过程也叫Currying,但是Currying的定义很严格,就是只有一个参数是一次传入,以便我们始终可以重用x=1的情况:f1=f(1)#callf1(2)f1(3)如果我们只是这样做,我们可以使用functools.partial来覆盖已经保持多元函数。fromfunctoolsimportpartialdeff(x,y):z=x**2+x+1returny*zf1=partial(f,1)f1(2)f1(3)但作为性能调整,这看起来像函数中嵌套函数的写法还有一个好处,就是上面说的“预计算”。比如上面的例子,很明显当x被带进来的时候,z就可以马上计算出来,而不用等y传入。而且我们发现每次调用f1的时候都会计算z。由此,我们可以看到函数的另一个特点是返回值。以下函数演示了返回值函数的所有技巧和优点。这种函数也称为PartialAppliedFunction或简称PartialFunction(注意与001数学中定义的偏函数的区别)。deff(x):#x存储我们需要的某种状态##所有可以提前计算的都放在这里z=x**2+x+1defhelper(y):##所有可以计算的later放在这里returny*zreturnhelper总结了函数作为一等公民的表现:我们只有函数作为水管和水进入水管两个概念;一个函数可以作为参数传递给另一个函数;一个函数可以作为另一个函数值返回;我们称至少具有2和3特征之一的函数为高阶函数。高阶函数的一些优点包括保持状态的能力,只需要函数和值的概念。不需要实例化、对象、可变变量、属性等更多概念;可以延迟计算以达到“懒惰”的最基本概念。可以提前计算,实现更高效的代码块重用。