第一:
def compose(f, g):
def wrapper(x):
return f(g(x))
wrapper.__name__ = f'compose({f.__name__}, {g.__name__})'
return wrapper
def ntimes(n):
def wrap(func):
if n == 1: return func
return compose(func, ntimes(n-1)(func))
return wrap
这应该很明显吧? ntimes(3) 是一个函数,它可以将任何函数与自身组合 3 次,所以 ntimes(3)(func)(x) 是 func(func(func(x)))。
现在,我们只需要在ntimes 上调用ntimes,并在两个级别使用相同的n。我可以编写一个 nntimes 函数,它的作用与 ntimes 相同,但为了多样化,让我们让它更平坦:
def nntimes(n, func, arg):
f = ntimes(n)
return f(f)(func)(arg)
所以nntimes(n, func, arg) 在ntimes(n) 上调用ntimes(n),这为您提供了一个函数,该函数组合了它的参数n**n 次,然后在arg 上调用该函数。
现在我们只需要一个函数来传入。print 不太好用,因为它返回None,所以你不能自己组合它。所以:
def printret(x):
print(x, end=' ')
return x
现在我们就叫它:
>>> nntimes(2, printret, 'Hi')
hi hi hi hi
>>> nntimes(3, printret, 'Hi')
hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi
如果您仍然无法理解发生了什么,也许这会有所帮助。让我们做一些比一般的nntimes 简单一点的事情,然后硬编码三个,然后打印出组合:
>>> thrice = ntimes(3)
>>> print(thrice(thrice)(printret).__name__)
compose(compose(compose(printret, compose(printret, printret)), compose(compose(printret, compose(printret, printret)), compose(printret, compose(printret, printret)))), compose(compose(compose(printret, compose(printret, printret)), compose(compose(printret, compose(printret, printret)), compose(printret, compose(printret, printret)))), compose(compose(printret, compose(printret, printret)), compose(compose(printret, compose(printret, printret)), compose(printret, compose(printret, printret))))))
所有这些括号!就像我死了,去了 Lisp!
如果您阅读Church numerals,您会发现我在这里有点作弊。编写一些简单的函数来对一个数字进行 Church 编码并对两个 Church 数字求幂,然后将其与我的代码进行比较。那么,我真的避免计算n**n的值了吗?
当然,您可以使用简单的平面递归和没有高阶函数或使用itertools 来更简单地做到这一点(好吧,您不允许使用内置函数,但 itertools 中的所有内容都带有源代码和/或文档中的“大致等效”功能,因此您可以复制它)。但这有什么乐趣呢?毕竟,如果你真的想要一个 Pythonic、简单或高效的版本,你只需遍历range(n**n)。我认为这个面试问题的重点是迫使你跳出 Pythonic 的框框思考。