如何多次撰写功能?

时间:2014-04-07 19:45:39

标签: python function

我应该写一个代码,它获取一个数学函数和一个数字,并给我一个输出一个由n次组成的函数。

例如,如果n=3我会得到f(f(f(x)))

当我运行我的代码时,我收到一个错误,我应该修复它?

运行示例:

>>> repeated(lambda x:x*x, 2)(5)
624
>>> repeated(lambda x:x*x, 4)(3)
43046721

这是我的代码:

def repeated(f, n):
    g=f
    for i in range(n):
        g=lambda x: (g(g(x)))
    return (g)

3 个答案:

答案 0 :(得分:4)

返回一个新函数,只有在调用时才重复应用

def repeated(f, n):
    def repeat(arg):
        return reduce(lambda r, g: g(r), [f] * n, arg)
    return repeat

reduce()方法使用f函数引用列表来创建正确数量的嵌套调用,从arg开始作为第一个参数。

演示:

>>> def repeated(f, n):
...     def repeat(arg):
...         return reduce(lambda r, g: g(r), [f] * n, arg)
...     return repeat
... 
>>> repeated(lambda x:x*x, 2)(5)
625
>>> repeated(lambda x:x*x, 4)(3)
43046721

不使用reduce()的版本为:

def repeated(f, n):
    def repeat(arg):
        res = arg
        for _ in range(n):
            res = f(res)
        return res
    return repeat

答案 1 :(得分:2)

根据您的任务的上下文(例如编程类),您可能有兴趣遵循直接的解决方案:

def repeated(f,  n):
  if n < 1:
    raise ValueError()
  elif n == 1:
    return f
  else:
    return lambda x: repeated(f, n-1)(f(x))

这是一个天真的递归解决方案,可以更直接地映射到需求。如果您已经了解更高级的功能,例如reduce我建议使用Martijn Pieters解决方案。不过这确实有效:

>>> repeated(lambda x:x*x, 2)(5)
625
>>> repeated(lambda x:x*x, 4)(3)
43046721

答案 2 :(得分:2)

我认为这是一个非常有趣的问题,我想在回答之前想几天。我已经创建了一组可推广的,pythonic(我认为)的方法,用于以问题中描述的方式组合函数。最通用的解决方案只是nest,它返回一个生成器,该生成器在初始参数上连续生成函数的嵌套值。其他所有内容都是基于此,但装饰器也可以使用上述解决方案之一来实现。

#!/usr/bin/env python

"""
Attempt to create a callable that can compose itself using operators
Also attempt to create a function-composition decorator.

    f(x) composed once is f(x)
    f(x) composed twice is f(f(x))
    f(x) composed thrice is f(f(f(x)))

This only makes sense at all if the function takes at least one argument:

    f() * 2 -> f(?)

But regardless of its arity, a function can only return exactly one value (even if that value is iterable). So I don't think it makes sense for the function to have >1 arity, either. I could unpack the result:

    f(x, y) * 2 -> f(*f(x, y))

But that introduces ambiguity -- not every iterable value should be unpacked. Must I inspect the function to tell its arity and decide whether or not to unpack on the fly? Too much work!

So for now, I just ignore cases other than 1-arity.
"""
def nest(func, arg):
    """Generator that calls a function on the results of the previous call.
    The initial call just returns the original argument."""
    while True:
        yield arg
        arg = func(arg)

def compose(n):
    """Return a decorator that composes the given function on itself n times."""
    if n < 1: raise ValueError
    def decorator(func):
        def nested(arg):
            gen = nest(func, arg)
            for i in range(n):
                next(gen)
            return next(gen)
        return nested
    return decorator

class composable(object):
    """A callable that can be added and multiplied."""
    def __init__(self, func):
        self.func = func
    def __add__(self, func2):
        """self(func2(x))"""
        def added(a):
            return self(func2(a))
        return composable(added)
    def __mul__(self, n):
        """self * 3 => self(self(self(a)))"""
        def nested(a):
            gen = nest(self, a)
            for i in range(n):
                next(gen)
            return next(gen)
        return composable(nested)
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

@compose(2)
def sq(x):
    return x*x

@compose(4)
def qu(x):
    return x*x

@composable
def add1(x):
    return x + 1

compset = composable(set)

assert (compset + str.split)('abc def') == set(['abc', 'def']), (compset + str.split)('abc def')
assert add1(1) == 2, add1(1)
assert (add1 + (lambda x: x * x))(4) == 17, (add1 + (lambda x: x * x))(4)
assert (add1 * 3)(5) == 8, (add1 * 3)(5)

assert 625 == sq(5), sq(5)
assert 43046721 == qu(3), qu(3)