I was working on project euler problem 14 and as a first attempt I whipped up this brute-force solution:
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)))
My question is about that lambda, I'm usually reluctant to use them but I don't know any elegant way to avoid it in this case. Is there something in functools or other to chain two callables, or any other neat alternative I'm missing?
解决方案
It would be lovely if there were a compose function -- perhaps in functools. There is not, nor do I expect there will be, alas. In the words of Raymond Hettinger,
It has been previously discussed and rejected in other forums. One of the issues is that the usual mathematical order is unintuitive and not self-documenting -- i.e. is compose(f,g) the same as f(g(x)) or g(f(x))? Also, it is already dirt simple to create your own compose function or to do the composition directly: h = lambda x: f(g(x)).
Here are two simple implementations of compose as a callable class that you may find useful though:
# 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)
Also, see the functional package, which inspired this very simple compose_many function of mine a while ago:
def compose(f1, f2):
def composition(*args, **kwargs):
return f1(f2(*args, **kwargs))
return composition
def compose_many(*funcs):
return reduce(compose, funcs)