在python中,将两个函数管道化为第三个二进制函数
我正在慢慢地尝试使用Python进行函数式编程,遇到了以下问题: 给定两个函数在python中,将两个函数管道化为第三个二进制函数,python,functional-programming,operators,toolz,Python,Functional Programming,Operators,Toolz,我正在慢慢地尝试使用Python进行函数式编程,遇到了以下问题: 给定两个函数f1和f2,我如何构造一个函数f,使这两个函数“以函数方式”与同一参数相乘 由于没有深入研究函数式编程,我有一个解决方案 f = lambda x : f1(x) * f2(x) 但它似乎不符合函数式编程的正确精神 我的下一次尝试是像这样使用mul和juxt操作符 >>> from tools import juxt >>> from operator import mul >
f1
和f2
,我如何构造一个函数f,使这两个函数“以函数方式”与同一参数相乘
由于没有深入研究函数式编程,我有一个解决方案
f = lambda x : f1(x) * f2(x)
但它似乎不符合函数式编程的正确精神
我的下一次尝试是像这样使用mul
和juxt
操作符
>>> from tools import juxt
>>> from operator import mul
>>> f = mul(juxt(f1,f2))
TypeError: op_mul expected 2 arguments, got 1
尝试使用*
拆分juxt
的元组输出也无效:
>>> f = mul(*juxt(f1, f2))
TypeError: mul() argument after * must be an iterable, not juxt
使用lambda
似乎再次奏效,但不知何故它挫败了整个目的
>>> temp = juxt(f_1, f_2)
>>> f = lambda x : mul(*temp(x))
也许我在这里太迂腐或不感谢(Python),但我觉得我在函数式编程中遗漏了一些非常重要或常规的东西
有没有更实用的方法来实现这一点?TL;DR这样的组合是Python和
tools
模块都不支持的一种基本操作(在某种意义上,它不能分解为其他高阶函数)。你需要自己实现它
您缺少的(或者更确切地说,Python和
工具模块缺少的)是应用程序函子的概念。为了理解这意味着什么,我们首先回顾一下tools
模块中的两个函数:
compose
可以将两个函数链接在一起。就是
compose(f,g) == lamba x: f(g(x))
curry
与部分应用相关:演示比解释更快:
curry(f)(x)(y) == f(x, y)
也就是说,curry(f)(x)
与partial(f,x)
基本相同;两者都取一个值y
,以返回值f(x,y)
此外,函子基本上是将函数映射到某个值上的一种方法。您无疑熟悉列表函子:
map(f, [a,b,c]) == [f(a), f(b), f(c)]
函数也是函子,但我们使用的不是map
,而是compose
。也就是说,将f
映射到g
上会生成compose(f,g)
现在,要将mul
、f1
和f2
组合成g=lambda x:g(f1(x),f2(x))
,似乎compose
和curry
都很有用。也就是说
lambda x: mul(f1(x), f2(x)) == lambda x: curry(mul)(f1(x))(f2(x))
及
(也就是说,curry
允许我们用另一个函数组成一个双参数函数。)
但从某种意义上说,构图是严格的线性运算;一个函数的输入来自另一个函数的输出。mul
和f1
的组合创建一个需要参数的函数,并返回一个需要相同参数的函数。如何将x
移出任一表达式的“中间”?我们需要的是一些
神秘功能foo
使
foo(f, g) = lambda x: f(x, g(x))
这使得函数将其参数同时传递给f
和g
,同时将结果g(x)
传递给f
。使用这样一个函数foo
,我们可以编写
lambda x: foo(compose(curry(mul), f1), f2)
并得到我们想要的结果
这就引出了应用函子的概念。它提供了必要的功能foo
def foo(f, g):
def _(x):
return f(x, g(x))
这结合了我们目前没有的构图和咖喱的概念
换句话说,foo
是一个独特的原始操作;你不能用组合本身来实现它。如果你真的想用操作符来实现这样的高阶函数,你可以为它做一个装饰器
class AddableFunction:
'''
Function decorator that lets (f+g)(x) = f(x) + g(x).
'''
def __init__(self, function):
self._function = function
def __call__(self, *args, **kwargs):
return self._function(*args, **kwargs)
def __add__(self, other):
return AddableFunction(lambda *args, **kwargs: self(*args, **kwargs) + other(*args, **kwargs))
@AddableFunction
def f(x):
return x ** 2
@AddableFunction
def g(x):
return x ** 3
print((f + g)(1)) # 2
print((f + g)(2)) # 12
print((f + g)(3)) # 36
我不明白你在问什么。首先,您不应该将lambda
表达式指定给一个名称,这会破坏它们的全部用途。只需使用完整的函数定义。但什么不是“功能程序的真正精神”?f=lambda x:f1(x)*f2(x)
有什么问题(除了使用lambda定义命名函数之外)没有问题。我不经常使用函数式编程,但我要说的是,您的第一个解决方案可能是解决您的问题的最具python风格和最常见的方法。@chickenNinja123将lambda表达式指定给一个名称显然不利于PEP8。这不一定会阻止你,但要意识到这一点。但是“逐点计算”是什么意思呢?即使在Haskell中,您也可能只编写fx=f1 x*f2 x
,除非您是函数的Applictive
实例的粉丝,在这种情况下,您将编写f=(*)f1 f2
。Python缺少的另一件事是处理函数的操作符。是的,您可以定义高阶函数(将函数作为参数和/或返回函数的函数),但没有运算符会使编写此类代码变得很麻烦<代码>f∘ g
比编写(f,g)
更容易阅读和书写(键盘问题除外)juxt(f,g)
可以改为f⊗ g
(从Unicode中选择一个提示性的、不一定理想的运算符)。(命名很难,但你可以习惯一个“足够好”的名字。)。。。
class AddableFunction:
'''
Function decorator that lets (f+g)(x) = f(x) + g(x).
'''
def __init__(self, function):
self._function = function
def __call__(self, *args, **kwargs):
return self._function(*args, **kwargs)
def __add__(self, other):
return AddableFunction(lambda *args, **kwargs: self(*args, **kwargs) + other(*args, **kwargs))
@AddableFunction
def f(x):
return x ** 2
@AddableFunction
def g(x):
return x ** 3
print((f + g)(1)) # 2
print((f + g)(2)) # 12
print((f + g)(3)) # 36