在 python 中将两个函数传递给第三个二元函数

Piping two functions to a third, binary function in python

我正在慢慢尝试进入 Python 中的函数式编程并遇到以下问题:

给定两个函数 f1f2,我如何构造一个函数 f 将这两个函数与相同的参数相乘 'in a functional way'?

没有深入研究函数式编程我有 一个 解决方案

f = lambda x : f1(x) * f2(x)

但它似乎不符合函数式编程的正确精神。

我的下一次尝试是像这样使用 muljuxt 运算符

>>> 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 模块所缺少的)是应用函子的概念。要理解that是什么意思,我们先回顾一下tools模块中的两个函数:

  1. compose 让您可以将两个函数链接在一起。也就是说,

    compose(f,g) == lamba x: f(g(x))
    
  2. curry与部分应用相关:演示比解释更快捷:

    curry(f)(x)(y) == f(x, y)
    

    curry(f)(x)partial(f, x)基本相同;都取值 y 到 return 值 f(x, y).

此外,仿函数 基本上是一种将函数映射到某个值的方法。你肯定熟悉列表仿函数:

map(f, [a,b,c]) == [f(a), f(b), f(c)]

函数也是仿函数,但我们使用compose而不是map。也就是说,将 f 映射到 g 会产生 compose(f, g).

现在,要将 mulf1f2 组合成 g = lambda x: g(f1(x), f2(x)),似乎 composecurry 都会有用。也就是说,

lambda x: mul(f1(x), f2(x)) == lambda x: curry(mul)(f1(x))(f2(x))

lambda x: mul(f1(x), f2(x)) == lambda x: compose(curry(mul), f1)(x)(f2(x))

(也就是说,curry 允许我们用另一个函数组合一个双参数函数。)

但是从某种意义上说,合成严格来说是一种线性操作;一个函数的输入来自另一个函数的输出。 mulf1 的组合创建了一个需要参数的函数 return 是一个需要相同参数的函数。我们如何将 x 移出任一表达式的 "middle"?我们需要的是一些 神秘函数 foo 这样

foo(f, g) = lambda x: f(x, g(x))

使一个函数将其参数传递给 fg,同时还将结果 g(x) 传递给 f。有了这样一个函数foo,我们可以写成

lambda x: foo(compose(curry(mul), f1), f2)

得到我们想要的结果。

这让我们想到了 applicative 仿函数。它提供了必要的功能foo

def foo(f, g):
   def _(x):
       return f(x, g(x))

它结合了我们目前没有的组合和柯里化的概念。

换句话说,foo是一个distinct原始操作;你不能根据组合本身来实现它。

如果你真的想为这样的高阶函数使用运算符,你可以为它做一个装饰器。

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