Sta*_*ess 23 python functional-programming composition function-composition
我有一个函数数组,我正在尝试生成一个函数,该函数由我的数组中的元素组成.我的方法是:
def compose(list):
if len(list) == 1:
return lambda x:list[0](x)
list.reverse()
final=lambda x:x
for f in list:
final=lambda x:f(final(x))
return final
Run Code Online (Sandbox Code Playgroud)
这种方法似乎不起作用,将不胜感激.
(我正在颠倒列表,因为这是我想要函数的组合顺序)
Suo*_*uor 28
最简单的方法是首先编写2个函数的组合:
def compose2(f, g):
return lambda *a, **kw: f(g(*a, **kw))
Run Code Online (Sandbox Code Playgroud)
然后reduce用来组成更多功能:
def compose(*fs):
return reduce(compose2, fs)
Run Code Online (Sandbox Code Playgroud)
pok*_*oke 16
def compose (*functions):
def inner(arg):
for f in reversed(functions):
arg = f(arg)
return arg
return inner
Run Code Online (Sandbox Code Playgroud)
例:
>>> def square (x):
return x ** 2
>>> def increment (x):
return x + 1
>>> def half (x):
return x / 2
>>> composed = compose(square, increment, half) # square(increment(half(x)))
>>> composed(5) # square(increment(half(5))) = square(increment(2.5)) = square(3.5) = 12,25
12.25
Run Code Online (Sandbox Code Playgroud)
use*_*342 11
它不起作用,因为您在循环中创建的所有匿名函数都引用相同的循环变量,因此共享其最终值.
作为快速修复,您可以将作业替换为:
final = lambda x, f=f, final=final: f(final(x))
Run Code Online (Sandbox Code Playgroud)
或者,您可以从函数返回lambda:
def wrap(accum, f):
return lambda x: f(accum(x))
...
final = wrap(final, f)
Run Code Online (Sandbox Code Playgroud)
要了解发生了什么,请尝试以下实验:
>>> l = [lambda: n for n in xrange(10)]
>>> [f() for f in l]
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
Run Code Online (Sandbox Code Playgroud)
这个结果令许多人感到惊讶,他们期待结果[0, 1, 2, ...].但是,所有lambdas都指向同一个n变量,并且都引用它的最终值,即9.在你的情况下,它的所有版本final都应该嵌套最终引用相同的f,更糟糕的是,相同的final.
SO中已经讨论了Python中的lambda和for循环的主题.
Aar*_*all 10
这是一个相当优雅的递归实现,为了清晰起见,它使用了Python 3的功能:
def strict_compose(*funcs):
*funcs, penultimate, last = funcs
if funcs:
penultimate = strict_compose(*funcs, penultimate)
return lambda *args, **kwargs: penultimate(last(*args, **kwargs))
Run Code Online (Sandbox Code Playgroud)
Python 2兼容版本:
def strict_compose2(*funcs):
if len(funcs) > 2:
penultimate = strict_compose2(*funcs[:-1])
else:
penultimate = funcs[-2]
return lambda *args, **kwargs: penultimate(funcs[-1](*args, **kwargs))
Run Code Online (Sandbox Code Playgroud)
这是一个使用延迟评估递归的早期版本:
def lazy_recursive_compose(*funcs):
def inner(*args, _funcs=funcs, **kwargs):
if len(_funcs) > 1:
return inner(_funcs[-1](*args, **kwargs), _funcs=_funcs[:-1])
else:
return _funcs[0](*args, **kwargs)
return inner
Run Code Online (Sandbox Code Playgroud)
两者似乎都会在每次递归调用时创建一个新的元组和参数的dict.
让我们测试一些这些实现并确定哪个是最高性能的,首先是一些单个参数函数(谢谢你戳):
def square(x):
return x ** 2
def increment(x):
return x + 1
def half(x):
return x / 2
Run Code Online (Sandbox Code Playgroud)
这是我们的实现,我怀疑我的迭代版本是第二高效的(手动编写自然会最快),但这可能部分是由于它避免了在函数之间传递任意数量的参数或关键字参数的困难 - 在大多数情况下我们只会看到传递的琐碎的一个论点.
from functools import reduce
def strict_recursive_compose(*funcs):
*funcs, penultimate, last = funcs
if funcs:
penultimate = strict_recursive_compose(*funcs, penultimate)
return lambda *args, **kwargs: penultimate(last(*args, **kwargs))
def strict_recursive_compose2(*funcs):
if len(funcs) > 2:
penultimate = strict_recursive_compose2(*funcs[:-1])
else:
penultimate = funcs[-2]
return lambda *args, **kwargs: penultimate(funcs[-1](*args, **kwargs))
def lazy_recursive_compose(*funcs):
def inner(*args, _funcs=funcs, **kwargs):
if len(_funcs) > 1:
return inner(_funcs[-1](*args, **kwargs), _funcs=_funcs[:-1])
else:
return _funcs[0](*args, **kwargs)
return inner
def iterative_compose(*functions):
"""my implementation, only accepts one argument."""
def inner(arg):
for f in reversed(functions):
arg = f(arg)
return arg
return inner
def _compose2(f, g):
return lambda *a, **kw: f(g(*a, **kw))
def reduce_compose1(*fs):
return reduce(_compose2, fs)
def reduce_compose2(*funcs):
"""bug fixed - added reversed()"""
return lambda x: reduce(lambda acc, f: f(acc), reversed(funcs), x)
Run Code Online (Sandbox Code Playgroud)
并测试这些:
import timeit
def manual_compose(n):
return square(increment(half(n)))
composes = (strict_recursive_compose, strict_recursive_compose2,
lazy_recursive_compose, iterative_compose,
reduce_compose1, reduce_compose2)
print('manual compose', min(timeit.repeat(lambda: manual_compose(5))), manual_compose(5))
for compose in composes:
fn = compose(square, increment, half)
result = min(timeit.repeat(lambda: fn(5)))
print(compose.__name__, result, fn(5))
Run Code Online (Sandbox Code Playgroud)
我们得到以下输出(Python 2和3中的相同幅度和比例):
manual compose 0.4963762479601428 12.25
strict_recursive_compose 0.6564744340721518 12.25
strict_recursive_compose2 0.7216697579715401 12.25
lazy_recursive_compose 1.260614730999805 12.25
iterative_compose 0.614982972969301 12.25
reduce_compose1 0.6768529079854488 12.25
reduce_compose2 0.9890829260693863 12.25
Run Code Online (Sandbox Code Playgroud)
我的期望得到了证实:当然,最快的是手动功能组合,然后是迭代实现.惰性递归版本要慢得多 - 可能是因为每个函数调用都会创建一个新的堆栈帧,并为每个函数创建一个新的函数元组.
为了更好也许更现实的比较,如果您删除**kwargs和更改*args到arg的功能,即用他们的将是更好的性能,我们可以更好地比较苹果苹果-从手工组成这里,一旁,reduce_compose1胜其次strict_recursive_compose:
manual compose 0.443808660027571 12.25
strict_recursive_compose 0.5409777010791004 12.25
strict_recursive_compose2 0.5698030130006373 12.25
lazy_recursive_compose 1.0381018499610946 12.25
iterative_compose 0.619289995986037 12.25
reduce_compose1 0.49532539502251893 12.25
reduce_compose2 0.9633988010464236 12.25
Run Code Online (Sandbox Code Playgroud)
只有一个arg的函数:
def strict_recursive_compose(*funcs):
*funcs, penultimate, last = funcs
if funcs:
penultimate = strict_recursive_compose(*funcs, penultimate)
return lambda arg: penultimate(last(arg))
def strict_recursive_compose2(*funcs):
if len(funcs) > 2:
penultimate = strict_recursive_compose2(*funcs[:-1])
else:
penultimate = funcs[-2]
return lambda arg: penultimate(funcs[-1](arg))
def lazy_recursive_compose(*funcs):
def inner(arg, _funcs=funcs):
if len(_funcs) > 1:
return inner(_funcs[-1](arg), _funcs=_funcs[:-1])
else:
return _funcs[0](arg)
return inner
def iterative_compose(*functions):
"""my implementation, only accepts one argument."""
def inner(arg):
for f in reversed(functions):
arg = f(arg)
return arg
return inner
def _compose2(f, g):
return lambda arg: f(g(arg))
def reduce_compose1(*fs):
return reduce(_compose2, fs)
def reduce_compose2(*funcs):
"""bug fixed - added reversed()"""
return lambda x: reduce(lambda acc, f: f(acc), reversed(funcs), x)
Run Code Online (Sandbox Code Playgroud)
一个班轮:
compose = lambda *F: reduce(lambda f, g: lambda x: f(g(x)), F)
Run Code Online (Sandbox Code Playgroud)
用法示例:
f1 = lambda x: x+3
f2 = lambda x: x*2
f3 = lambda x: x-1
g = compose(f1, f2, f3)
assert(g(7) == 15)
Run Code Online (Sandbox Code Playgroud)
我发现的最可靠的实现是在 3rd party library 中toolz。该compose库中的函数还处理用于组合函数的文档字符串。
的源代码是免费提供的。下面是一个简单的用法示例。
from toolz import compose
def f(x):
return x+1
def g(x):
return x*2
def h(x):
return x+3
res = compose(f, g, h)(5) # 17
Run Code Online (Sandbox Code Playgroud)
您还可以创建函数数组并使用reduce:
def f1(x): return x+1
def f2(x): return x+2
def f3(x): return x+3
x = 5
# Will print f3(f2(f1(x)))
print reduce(lambda acc, x: x(acc), [f1, f2, f3], x)
# As a function:
def compose(*funcs):
return lambda x: reduce(lambda acc, f: f(acc), funcs, x)
f = compose(f1, f2, f3)
Run Code Online (Sandbox Code Playgroud)
pip install funcoperators是另一个实现它的库,它允许中缀表示法:
from funcoperators import compose
# display = lambda x: hex(ord(list(x)))
display = hex *compose* ord *compose* list
# also works as a function
display = compose(hex, ord, list)
Run Code Online (Sandbox Code Playgroud)
pip 安装 funcoperators https://pypi.org/project/funcoperators/
免责声明:我是该模块的创建者
| 归档时间: |
|
| 查看次数: |
22996 次 |
| 最近记录: |