Estaba trabajando en el problema 14 del proyecto Euler y, como primer bash, preparé esta solución de fuerza bruta:
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)))
Mi pregunta es acerca de esa lambda, por lo general soy reacio a usarlos, pero no conozco ninguna forma elegante de evitarlo en este caso. ¿Hay algo en functools
u otro para encadenar dos callables, o alguna otra alternativa limpia que me esté perdiendo?
Sería encantador si hubiera una función de compose
, tal vez en functools
. No hay, ni espero que haya, por desgracia . En las palabras de Raymond Hettinger,
Ha sido discutido previamente y rechazado en otros foros. Uno de los problemas es que el orden matemático habitual no es intuitivo y no se autodocumenta, es decir, ¿
compose(f,g)
lo mismo quef(g(x))
og(f(x))
? Además, ya es muy sencillo crear su propia función de composición o hacer la composición directamente:h = lambda x: f(g(x))
.
Aquí hay dos implementaciones simples de compose
como una clase invocable que puede encontrar útil, sin embargo:
# 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)
También, vea el paquete functional
, que inspiró esta muy simple función compose_many
mía hace un tiempo:
def compose(f1, f2): def composition(*args, **kwargs): return f1(f2(*args, **kwargs)) return composition def compose_many(*funcs): return reduce(compose, funcs)
Podría ser más sintético escribir esto como un generador:
def p014(): length, n = max( (len(collatz(n)), n) for n in xrange(1, 10**6) ) return n