python - 我们可以在列表推导式中使用嵌套 for 循环的幕后原因是什么

标签 python loops nested list-comprehension

我一直在研究列表推导式,但有件事让我停顿了好几天。

简单的列表理解具有以下形式

[可迭代项的表达式]

等效的for循环是

li=[]
for item in iterable
    li.append(item)

如果我是对的,列表理解通常会迭代可迭代对象,计算每次迭代的表达式,然后将其附加到列表中。

for 循环内发生的任何事情都写在 liscomp 的开头。

我们可以认为,在一个listcomp中,Python只允许一个表达式,并且for循环的套装只允许有一个if子句或嵌套的for 循环。

引用我正在读的一本书,其中指出

Since list comprehensions produce lists, that is, iterables, and since the syntax for list comprehensions requires an iterable, it is possible to nest list comprehensions. This is the equivalent of having nested for … in loops.

这混淆了我的理解。

这是否说明了使用像 [s+z for s in iterable_1 for z in iterable_2] 这样的 listcomp 的原因

有人可以解释一下这是什么意思吗?

最佳答案

您的第一个翻译应该是

li=[]
for item in iterable: 
    li.append( expression )

您的示例[s+z for s in iterable_1 for z in iterable_2] 翻译为

li=[]
for s in iterable_1:
    for z in iterable_2:
        li.append(s+z)

恭喜,您发现了...Monad!本质上就是您所描述的,广义嵌套循环。

嵌套循环只会产生简单的结果流。嵌套列表在扁平化时也会变成普通的元素流。这就是相似之处。 惰性 append 非常类似于 yield

每个 monad 类型都是通过其实现 flatMap 函数的版本来定义的,该函数是一个映射,后跟结果嵌套结构的展平,将新值拼接到包含结构中。就地。每个嵌套级别的嵌套结构的扁平化允许扁平化任意嵌套深度:

M[ M[a]]  ==>  M[a]

M[ M[ M[a]]]  ==>   # flatten the two outer layers first:
                       M[ M[ M[a]]]
                       M[    M[a] ]  ==>  M[a]
               OR:
               ==>   # flatten the two inner layers first:
                       M[ M[ M[a]]]
                       M[ M[   a ]]  ==>  M[a]

看出区别了吗?没有啊!任何执行上述操作的类型都是“monad”。就像列表一样,

    [ [ [a, b], [c] ], [], [ [], [d, e, f] ] ]
    [   [a, b], [c]  ,       [], [d, e, f]   ]
    [    a, b ,  c   ,            d, e, f    ]

    # or,
    [ [ [a, b], [c] ], [], [ [], [d, e, f] ] ]
    [ [  a, b ,  c  ], [], [      d, e, f  ] ]
    [    a, b ,  c   ,            d, e, f    ]

当我们在最深层关注内部的实际值,而忽略包含的“壳”结构时,实际上没有什么区别。在这个阶段,所有的“装饰性”噪音都没有意义了。它对我们如何到达最深层次产生了影响;但当它在那里时,它已经发挥了它的作用,并且不再存在。

仍然保留外层,将值封装在一种特定类型的容器/生产者/等值中,通过代表的值的包装类型维护内部值的封装。所有的“扁平化”都是“在内部”、“在包装下”完成的。当可以以任何顺序进行展平并产生相同的最终结果时,它是明确定义的,因此可以不指定顺序。与乘法一样,3*4*2 = (3*4)*2 = 3*(4*2)。对于像数字这样的普通值,这在数学中被称为 Monoid,但是对于像列表这样的高阶封装值,它被称为 Monad(可以被称为高阶幺半群,但是据我所知,不是)。

循环也是如此,它可以嵌套到任意深度——两个、三个,无论什么,都没关系。整个结构仍在一一产生其结果,这些是最内层循环正在一一产生的结果。

就是我们可以在列表推导式中使用嵌套for循环的幕后原因。或者,用一种奇特的方式说同样的事情,这是因为列表推导就像一元操作链(并且可以这样翻译)。


固定的嵌套循环,即事先已知的循环(如上面的列表示例所示),对应于所谓的应用(又名幺半群)仿函数。旁注,对于列表来说,有两种不同的方法可以将两个嵌套列表组合成一个 - 一种方法是将它们按顺序组合,从而产生类似“叉积”的组合,而另一种方法是将它们组合起来。方式并行,导致类似压缩的组合,就像数学中的点积(没有最终的求和)。

具有可修改生成值的非嵌套“循环”(即生成生成器)只是普通的仿函数。

Monad 的关键区别在于能够根据其上方循环产生的来计算嵌套循环。或者就您而言,

li=[]
for s in iterable_1:
    for z in foo(s):      # for some appropriate foo()
        li.append(s+z)

或等效的[s+z for s in iterable_1 for z in foo(s)]

一般而言,这相当于解释器模式。

关于python - 我们可以在列表推导式中使用嵌套 for 循环的幕后原因是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61402412/

相关文章:

python - 使用 Python 3.8 的 Jupyter Notebook - NotImplementedError

python - 在 Python 中打印表格

c# - C# 中的分隔嵌套括号

java - java中的嵌套接口(interface)

python - 选定单元格的行号和列号

python - 删除 CVS 文件中的字符以获得列的平均值

python - 替换 BeautifulSoup 迭代器中的字符串提前退出?

Python:迭代一个列表,但我想在循环中添加到该列表并继续迭代添加的内容。

javascript - JavaScript 匿名函数的参数

c - 如果无法获得 websocket 连接则退出应用程序的逻辑