我有以下功能
def handle(x):
if is_odd(x):
return x * 3 + 1
return x // 2
我希望运行它,将句柄(x)返回的值反复输入到自身,直到它返回1
即。 handle(... handle(handle(handle(x)))...)直到链中的一个返回1
repeat(handle(x), lambda x: x != 1)
我想写这样的东西:
(let n = handle(x) until n != 1)
基本上我想存储句柄的输出,直到我得到1。
expression(handle(10)) = [5,
16,
8,
4,
2,
1]
是否有与此相当的生成器表达式?或者我可以使用functools.repeat方法吗?
为了清楚起见,我想避免直接使用带有yield或递归的for循环。我想在内置功能中使用python来解决这个问题,不包括for循环/递归。
答案 0 :(得分:2)
使用Collatz Conjecture不需要任何神奇的方法,只需编写常规循环
def expression(n):
result = [n]
while n > 1:
n = handle(n)
result.append(n)
return result
特别是你可以把它变成一个发电机
def expression_gen(n):
yield n
while n > 1:
n = handle(n)
yield n
显然你可以通过
进行参数化def expression_gen(n, handle, end):
yield n
while not end(n):
n = handle(n)
yield n
你可以用lambda表达式作为句柄/结束来调用它。
答案 1 :(得分:2)
我猜你可以使用itertools.accumulate
和itertools.takewhile
:
from itertools import accumulate, takewhile, repeat
def collatz(n):
seq = accumulate(repeat(n), lambda x,_: handle(x))
return list(takewhile(lambda x: x!=1, seq)) + [1]
给出了
>>> collatz(5)
[5, 16, 8, 4, 2, 1]
>>> collatz(17)
[17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
但我有一个共同观点,即我不明白这一点。甚至官方文档也列出了很多recipes,它们可以很方便地使用非内置的iterables;我不明白为什么你不能只定义像
这样的东西def repeat_until(fn, x, cond):
while True:
yield x
if cond(x): break
x = fn(x)
继续你的一天。我知道你已经说过你不想出于某种原因明确使用yield
,但仅仅因为你说你想要的东西并不意味着你想要的东西是有意义的。 : - )
答案 2 :(得分:1)
首先,我认为这是使用生成器和while循环的规范方法。
def handle(x):
while x != 1:
x = x*3 + 1 if x%2 else x//2
yield x
演示:
>>> list(handle(10))
[5, 16, 8, 4, 2, 1]
如果您的Python版本支持它,那么接近您正在寻找的另一个选项是使用递归和yield from
语法:
def handle(x):
if x!= 1:
x = x*3 + 1 if x%2 else x//2
yield x
yield from handle(x)
但是这种方法存在问题,这正在炸毁堆栈:
>>> for x in handle(1E1000): pass
[...]
RecursionError: maximum recursion depth exceeded in comparison
答案 3 :(得分:0)
您可以使用while循环:
def handle(num):
while num > 1:
if num % 2 == 0:
num //= 2
print(num)
else:
num = 3 * num + 1
print(num)
if __name__ == '__main__':
handle(10)