【问题标题】:History-Dependent List-Comprehension or how to express `[f(x, this) for x in X if g(x, this)]`?History-Dependent List-Comprehension 或如何表达`[f(x, this) for x in X if g(x, this)]`?
【发布时间】:2021-07-25 16:21:34
【问题描述】:

Python 的 list-comprehensions 非常有用,因为它们允许我们将常见的模式写入简单、简洁、易读的单行代码中:

loop with filter loop with break
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)]

考虑允许过滤器/中断依赖于所有先前生成的元素的稍微更一般的模式:

loop with history-dependent filter loop with history-dependent 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))

问题:是否有可能将这些模式也转化为列表理解?

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



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

似乎需要一种全新的语法

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

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

loop with history-dependent filter loop with history-dependent 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))
[f(x) <b>for</b> x <b>in</b> xs <b>if</b> g(x, <b>this</b>)] [f(x) <b>for</b> x <b>in</b> xs <b>while</b> g(x, <b>this</b>)]

或者更一般的

loop with history-dependent filter and yield loop with history-dependent break and yield
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⁽ᵏ⁾)}

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

【问题讨论】:

  • @AlexWaygood 问题中提出的this 的语义没有基本障碍 - 被理解的列表确实在执行理解时存在,因为最终是一个列表理解是通过创建一个空列表,运行一个循环(可能检查条件并)计算附加到列表中的一些结果,然后返回列表来执行的。因此,在检查条件并计算结果时,列表确实存在,并且语言允许条件和/或结果访问对它的引用并非不可能。
  • 谢谢!我不知道理解是如何实现的。原谅我的无知。我仍然认为在 Python 中实现是不可能的(而且可能是不可取的?)(我知道这是不同的一点)。对我的口味来说有点太神奇了。
  • @kaya3 你也可以尝试实际find the list并使用它。

标签: python list-comprehension


【解决方案1】:

通过使函数f 和/或g 有状态(通常您希望将状态封装在某个类的实例中),可以将它们转换为列表解析。例如,这是一个列表推导式,它贪婪地构建原始列表的升序子序列:

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 的未记录行为),但它确实实现了您想要做的事情。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-15
    • 2017-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-26
    • 1970-01-01
    • 2023-02-23
    相关资源
    最近更新 更多