Python, исключающий лямбду для ключа, который требует двух вызовов (состав функции)

Я работал над проблемой проекта Эйлера 14 и в качестве первой попытки я выбрал это грубое решение:

def collatz(n, memo={1: [1]}):
    if n not in memo:
        memo[n] = [n] + collatz(3 * n + 1 if n % 2 else n // 2)
    return memo[n]

def p014():
    return max(xrange(1, 10**6), key=lambda n: len(collatz(n)))

Мой вопрос об этой лямбде, я обычно не хочу их использовать, но я не знаю элегантного способа избежать этого в этом случае. Есть ли что-то в functools или другой, чтобы связать два вызываемых, или любую другую аккуратную альтернативу, которую мне не хватает?

2 ответа

Решение

Было бы прекрасно, если бы compose функция - возможно, в functools, Нет, и я не ожидаю, что будет, увы. По словам Раймона Хеттингера,

Ранее это обсуждалось и отклонялось на других форумах. Одна из проблем заключается в том, что обычный математический порядок не интуитивен и не самодокументирован, т.е. compose(f,g) такой же как f(g(x)) или же g(f(x))? Кроме того, уже очень просто создать свою собственную функцию композиции или сделать композицию напрямую: h = lambda x: f(g(x)),

Вот две простые реализации compose как вызываемый класс, который может оказаться полезным:

# Scott Daniels, http://code.activestate.com/recipes/52902-function-composition/
# Lightly edited for style.
class Compose(object):
    '''Compose functions. compose(f,g,x...)(y...) = f(g(y...),x...))'''
    def __init__(self, f, g, *args, **kwargs):
        self.f = f
        self.g = g
        self.pending = args[:]
        self.kwargs = kwargs.copy()

    def __call__(self, *args, **kwargs):
        return self.f(self.g(*args, **kwargs), *self.pending, **self.kwargs)


class Starcompose:
    '''Compose functions. Starcompose(f,g,x...)(y...) = f(*g(y...),x...))'''
    TupleType = type(())

    def __init__(self, f, g, *args, **kwargs):
        self.f = f
        self.g = g
        self.pending = args[:]
        self.kwargs = kwargs.copy()

    def __call__(self, *args, **kwargs):
        mid = self.g(*args, **kwargs)
        if isinstance(mid, self.TupleType):
            return self.f(*(mid + self.pending), **self.kwargs)
        return self.f(mid, *self.pending, **self.kwargs)

Также смотрите functional пакет, который вдохновил это очень просто compose_many Моя функция некоторое время назад:

def compose(f1, f2):
    def composition(*args, **kwargs):
        return f1(f2(*args, **kwargs))
    return composition

def compose_many(*funcs):
    return reduce(compose, funcs)

Это может быть более питонным, чтобы написать это как генератор:

def p014():
    length, n = max(
        (len(collatz(n)), n)
        for n in xrange(1, 10**6)
    )
    return n
Другие вопросы по тегам