History-Dependent List-Comprehension 或如何表达 `[f(x, this) for x in X if g(x, this)]`?

History-Dependent List-Comprehension or how to express `[f(x, this) for x in X if g(x, this)]`?

Python 的列表推导非常有用,因为它们允许我们将常见的模式写入简单、简洁、可读性极强的单行代码中:

带过滤器的循环 带中断的循环
l = []
<b>for</b> x <b>in</b> xs:
<b>if</b> g(x):
l.append(f(x))
l = []
<b>for</b> x <b>in</b> xs:
<b>if</b> <b>not</b> g(x):
<b>break</b>
l.append(f(x))
[f(x) <b>for</b> x <b>in</b> xs <b>if</b> g(x)] [f(x) <b>for</b> x <b>in</b> takewhile(g, x)]

考虑允许 filter/break 依赖于所有先前生成的元素的稍微更通用的模式:

使用依赖于历史的过滤器循环 循环与历史相关的中断
l = []
<b>for</b> x <b>in</b> xs:
<b>if</b> g(x, l):
l.append(f(x))
l = []
<b>for</b> x <b>in</b> xs:
<b>if</b> <b>not</b> g(x, l):
<b>break</b>
l.append(f(x))

Question: Is it at all possible to translate these patterns into list-comprehensions as well?

对我来说,答案似乎是否定的/它只适用于 g 的非常特殊的情况(比如 ),因为其中一个需要能够引用列表创建时。




问题结束//有些语无伦次:

似乎需要一种全新的语法,类似于

[f(x) <b>for</b> x <b>in</b> xs <b>if</b> g(x, <b>这个</b>)]

其中 <b>this</b> 是新关键字的符号占位符,可以从上下文中引用外部对象。我不知道这在原则上是否完全可行,但能够编写

会很酷
使用依赖于历史的过滤器循环 循环与历史相关的中断
l = []
<b>for</b> x <b>in</b> xs:
<b>if</b> g(x, l):
l.append(f(x))
l = []
<b>for</b> x <b>in</b> xs:
<b>if</b> <b>not</b> g(x, l):
<b>break</b>
l.append(f(x))
[f(x) <b>for</b> x <b>in</b> xs <b>if</b> g(x, <b>这个</b>)] [f(x) <b>for</b> x <b>in</b> xs <b>while</b> g(x, <b>这个</b>)]

或者更一般的

使用依赖于历史的过滤器和产量循环 具有依赖于历史的中断和收益的循环
l = []
<b>for</b> x <b>in</b> xs:
<b>if</b> g(x, l):
l.append(f(x, l))
l = []
<b>for</b> x <b>in</b> xs:
<b>if</b> <b>not</b> g(x, l):
<b>break</b>
l.append(f(x, l))
[f(x, <b>this</b>) <b>for</b> x <b>in</b> xs <b>if</b> g(x, <b>this</b>)] [f(x, <b>this</b>) <b>for</b> x <b>in</b> xs <b>while</b> g(x, <b>this</b>)]

其中下一个元素和列表理解的退出条件都允许依赖于所有 先前生成的元素。在 set-builder notation 中,这两个结构将转换为

S⁽ⁿ⁺¹⁾ = { f(xₖ, S⁽ᵏ⁾) ∣ xₖ∈{x₁,…,xₙ} ∧ g(xₖ, S⁽ᵏ⁾) } 
       = S⁽ⁿ⁾ ∪ {f(xₙ, S⁽ⁿ⁾) ∣ g(xₙ, S⁽ⁿ⁾)}

S⁽ⁿ⁺¹⁾ = { f(xₖ, S⁽ᵏ⁾) ∣ xₖ∈{x₁,…,xₙ} ∧ ∀l≤k g(xₗ, S⁽ˡ⁾) } 
       = S⁽ⁿ⁾ ∪ {f(xₙ, S⁽ⁿ⁾) ∣ ∀k≤n g(xₖ, S⁽ᵏ⁾)}

是否有任何语言具有这样的功能?

可以通过使函数 f and/or g 有状态(通常您希望将状态封装在某些 class).例如,这是一个列表理解,它贪婪地构建原始列表的升序子序列:

class State:
    def __init__(self):
        self.x = None
    def is_ascending(self, x):
        if self.x is None or x > self.x:
            self.x = x
            return True
        else:
            return False

nums = [1, 2, 1, 3, 1, 4, 1, 5, 1, 6]
state = State()

print([x for x in nums if state.is_ascending(x)])
# [1, 2, 3, 4, 5, 6]

在最坏的情况下,状态存储到目前为止添加的所有元素,这与 for 循环相比是多余的,但尽管如此,答案是您可以将其写成列表理解。


作为替代方案,您可以这样做:

result = []
result.extend(len(result) for i in range(10) if 5 not in result)
print(result)
# [0, 1, 2, 3, 4, 5]

请注意,extend 必须使用延迟计算的生成器表达式来调用,以便它生成的每个元素都可以添加到 result(因此 result 将具有生成下一个元素之前的正确状态)。我不认为这是编写代码的好方法(而且它似乎取决于 extend 的未记录行为),但它确实实现了你正在尝试做的事情。