【问题标题】:Creating a generator expression for hailstone sequence为冰雹序列创建生成器表达式
【发布时间】:2020-09-06 01:53:23
【问题描述】:

创建了以下生成器函数:

def hailstone(n):
    yield n
    while n > 1:
        n = n // 2 if n % 2 == 0 else n * 3 + 1
        yield n

作为冰雹序列的生成器函数对我有用,现在我尝试从生成器表达式(单行)生成相同的输出。

我正努力朝着这个方向发展:

hailstone = (num // 2 if num % 2 == 0 else num * 3 + 1 for i in range("something here"))

其中 num 作为第一个数字传递。 我注意到使用范围可能不是正确的路径,但我需要一个停止条件。

我想弄清楚的主要事情:

  1. 如何将 next() 输出传递回生成器以生成序列?
  2. 如何提供停止条件?

对执行此任务有帮助吗?

【问题讨论】:

  • 没有很好的方法来转换这样的 while 循环,它依赖于主体内的值作为停止生成器表达式的条件。就用这个吧。
  • 要使用生成器表达式,您需要生成一个无限序列,然后使用take_while 函数或其他东西。当您提前知道要进行多少次迭代(或至少可能的最大迭代次数)时,通常会使用生成器表达式。对于列表推导/生成器表达式来说,迭代直到一个条件是一个糟糕的用例,因为这些构造是为了迭代现有的可迭代对象。
  • 是的,理解结构是围绕在可迭代对象上表达映射/过滤转换而设计的。它们并不意味着将所有东西都变成一条线。 Python 是一种足够动态的语言,可以让你做各种恶作剧,所以我想有一些方法可以完成你想要的,但你绝对不应该那样做。跨度>
  • hailstone = lambda num: (num if i == 0 else (num:=num // 2) if num % 2 == 0 else (num:=num * 3 + 1) for i, _ in enumerate(iter(lambda: num, 1)))太丑了,别用了。
  • @AndrejKese 巧妙地使用了 iter 的两个参数形式,但天哪,这太恶心了iter(lambda: num, 1)。 ...我想我更喜欢takewhile,我真的不喜欢那个功能

标签: python generator list-comprehension


【解决方案1】:

正如在 cmets 中所指出的,生成器表达式不是这里工作的正确工具。生成器表达式和列表推导旨在从现有的可迭代对象生成可迭代对象。它们在这里不太适合,因为您没有现有的可迭代对象来迭代。

在理解任何使用生成器表达式都会被强制执行的情况下,我会通过迭代无限可迭代来做到这一点,然后在满足条件时进行:

from itertools import count, takewhile

def next_hailstone(n):
    return n // 2 if n % 2 == 0 else n * 3 + 1

last = 1000  # Starting number
infinite_seq = (last := next_hailstone(last) for _ in count(0))
finite_seq = takewhile(lambda n: n > 1, infinite_seq)

print(list(finite_seq))

注意事项:

  • 这需要 Python 3.8+ 才能使用 assignment expressions (:=)。如果没有赋值表达式,就很难在一次迭代到下一次迭代中保持状态,这是这个问题所必需的(另一个迹象表明,这是该工作的错误工具)。

  • count(0) 只是为了提供无限迭代。您可以将其替换为任何其他可以永远迭代的东西(例如包含 yield None 的无限 while 循环的生成器函数)。

  • 我在这里写的基本上是一个简化。完成此类任务的内置工具,如果您/您的老师坚持要花哨的话,是reduce。我是一名函数式程序员,我喜欢reduce。但实际上,大多数情况下,一个简单的循环会更合适。

  • 请不要在实际代码中这样做。同样,生成器表达式和列表推导等结构旨在从现有的可迭代对象中创建新的可迭代对象。其他任何东西都可以说是对它们的滥用。

【讨论】:

    【解决方案2】:

    使用递归的冰雹序列的生成器表达式

    def hailstone_sequence(input_num):
        return [input_num] if input_num == 1 else ([input_num] + hailstone_sequence(input_num // 2) if input_num % 2 == 0 else [input_num] + hailstone_sequence(3 * input_num + 1))
    
    
    for i in hailstone_sequence(11):
        print(i)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-28
      相关资源
      最近更新 更多