在 python 中将两个函数传递给第三个二元函数
Piping two functions to a third, binary function in python
我正在慢慢尝试进入 Python 中的函数式编程并遇到以下问题:
给定两个函数 f1
和 f2
,我如何构造一个函数 f 将这两个函数与相同的参数相乘 'in a functional way'?
没有深入研究函数式编程我有 一个 解决方案
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
模块所缺少的)是应用函子的概念。要理解that是什么意思,我们先回顾一下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
到 return 值 f(x, y)
.
此外,仿函数 基本上是一种将函数映射到某个值的方法。你肯定熟悉列表仿函数:
map(f, [a,b,c]) == [f(a), f(b), f(c)]
函数也是仿函数,但我们使用compose
而不是map
。也就是说,将 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))
和
lambda x: mul(f1(x), f2(x)) == lambda x: compose(curry(mul), f1)(x)(f2(x))
(也就是说,curry
允许我们用另一个函数组合一个双参数函数。)
但是从某种意义上说,合成严格来说是一种线性操作;一个函数的输入来自另一个函数的输出。 mul
和 f1
的组合创建了一个需要参数的函数 和 return 是一个需要相同参数的函数。我们如何将 x
移出任一表达式的 "middle"?我们需要的是一些
神秘函数 foo
这样
foo(f, g) = lambda x: f(x, g(x))
使一个函数将其参数传递给 f
和 g
,同时还将结果 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
我正在慢慢尝试进入 Python 中的函数式编程并遇到以下问题:
给定两个函数 f1
和 f2
,我如何构造一个函数 f 将这两个函数与相同的参数相乘 'in a functional way'?
没有深入研究函数式编程我有 一个 解决方案
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
模块所缺少的)是应用函子的概念。要理解that是什么意思,我们先回顾一下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
到 return 值f(x, y)
.
此外,仿函数 基本上是一种将函数映射到某个值的方法。你肯定熟悉列表仿函数:
map(f, [a,b,c]) == [f(a), f(b), f(c)]
函数也是仿函数,但我们使用compose
而不是map
。也就是说,将 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))
和
lambda x: mul(f1(x), f2(x)) == lambda x: compose(curry(mul), f1)(x)(f2(x))
(也就是说,curry
允许我们用另一个函数组合一个双参数函数。)
但是从某种意义上说,合成严格来说是一种线性操作;一个函数的输入来自另一个函数的输出。 mul
和 f1
的组合创建了一个需要参数的函数 和 return 是一个需要相同参数的函数。我们如何将 x
移出任一表达式的 "middle"?我们需要的是一些
神秘函数 foo
这样
foo(f, g) = lambda x: f(x, g(x))
使一个函数将其参数传递给 f
和 g
,同时还将结果 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