在解决矩阵旋转问题时,我遇到了一个解决方案。问题要求将矩阵旋转90度。
def rotateImage(a):
rows = len(a)
cols = len(a[0])
a=a[::-1]
return [[m[i] for m in a] for i in range(cols)]
有人可以帮助我将这个列表理解分解为常规for循环并理解程序的流程
答案 0 :(得分:1)
你有一个嵌套列表理解,一个外部列表理解为每次迭代创建一个新列表。
您可以通过从左到右阅读for
和if
组件作为嵌套块来解构任何列表理解。第一个for
之前的 Everything 是生成列表元素的表达式。
所以基本结构是:
[<expression> for <targets> in <iterable> <optionally more if and for>]
总是可以转换为常规循环:
_result = []
for <targets> in <iterable>:
<optionally more if and for blocks, add a new level each time>
_result.append(<expression>)
我在_result
中添加了作为生成列表的显式名称;请注意,每次迭代都会附加<expression>
的结果。
您的示例分解为两个列表推导。外循环是这样的:
[[m[i] for m in a] for i in range(cols)]
# \ / | \ /
# <expression> <targets> <iterable>
所以[m[i] for m in a]
就是这里的表达!把它写出来就成了:
_result_outer = []
for i in range(cols):
_result_outer.append([m[i] for m in a])
内部列表理解很简单:
_result_inner = []
for m in a:
_result_inner.append(m[i])
如果你将这两者放在一起,你会得到:
_result_outer = []
for i in range(cols):
_result_inner = []
for m in a:
_result_inner.append(m[i])
_result_outer.append(_result_inner)
现在,您可以看到每次在内部循环中使用外部循环中的i
目标。因为i
遍历列数,但m
是一行,所以首先会生成一个列表,其中包含第一列,第二列,第三列,第三列等所有值。
列表理解之前的行颠倒行:
a = a[::-1]
这将占用所有行,从最后一行到第一行(向后退步,步长为-1
)。这使得列表推导以相反的顺序循环遍历行。
代码可以清理和改进一点。例如,rows
变量从不使用,我们可以使用reversed()
function以相反的顺序循环遍历列表而不创建副本:
def rotateImage(a):
cols = len(a[0])
return [[m[i] for m in reversed(a)] for i in range(cols)]
另一种转置矩阵(将列移动到行)的方法是使用zip()
function将所有行作为单独的参数应用,并使用*<expression>
调用语法:
def rotateImage(a):
return [list(r) for r in zip(*reversed(a))]
这仍然使用列表推导,因为zip()
生成可迭代对象,而不是列表,每次迭代时都会得到一个元组。上面的列表理解将整个结果转换回列表列表。
如果您想要到位,我建议您阅读Jack在使用Python旋转矩阵时所写的非常出色的答案;他们对如何编码的解释是首屈一指的:How do you rotate a two dimensional array?
答案 1 :(得分:0)
a=a[::-1]
只是反过来了。列表理解是:
res = []
for i in range(cols):
tmp = []
for m in a:
tmp.append(m[i])
res.append(tmp)
return res
**** **** EDIT
这里基本上发生的是每个列索引i
算法遍历所有行m
(从最后到第一个)并附加m[i]
(第i个)第{m行中的值)到tmp
,然后作为一行附加到res
。因此,每个反向列都会作为一行添加到res
。
所以对于像:
这样的矩阵1 2 3 4
5 6 7 8
9 1 0 4
第一个内循环迭代将导致tmp=[9, 5, 1]