在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 >

我正在慢慢地尝试使用Python进行函数式编程,遇到了以下问题:

给定两个函数
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