【问题标题】:tiered while loops with multiple outputs具有多个输出的分层 while 循环
【发布时间】:2015-05-23 03:42:47
【问题描述】:

我正在尝试学习数组。我知道python有列表,而不是数组,但想法是一样的。我有一个像数组一样设置的列表列表,我正在尝试修改它们以获得随机艺术乐趣,但我只能从这段代码中得到一个随机数。

##prior list creation code making a large list of zeros called "array"###
while 3 not in array:
    i= random.randint(1,9)
    j= random.randint(1,28)
    if (i%3)!=0 and (j%7)!=0:
        if 5 in array:
            array[i][j]=3
            return array

        elif 3 in array:
            array[i][j]=4
            return array

        else:
            array[i][j]=5
            amy=(i, j)
            return array
    continue
##the resulting list called "array" does not chnange any zero to any number except one to "5"##

我已经删除了使数组填充零的代码。唯一会显示的数字是 5...理想情况下,我会让每个数字在每次运行时只显示一次,但在不同的位置

我做错了什么?我不完全理解数组,所以可能就是这样,但我在搜索我认为可能存在的问题时遇到了麻烦。您能提供的任何帮助都会很棒!

编辑: 抱歉忘记了,该数组的大小适合保存数据(9 行 x 28 列),并且它不会引发任何错误或异常......在我发布之前应该已经存在。

【问题讨论】:

  • 您的 if 条件 if (i%3)!=0 and (j%7)!=0 没有任何意义。你能解释一下你打算用它做什么吗?
  • 另外三个continue 语句都是死代码,因为它们前面有一个return 语句。
  • 另外,Python 的 in 运算符看起来只在集合中完成了一个级别:3 in [[3],[3],[3]] == False,因此您的 while 循环的条件存在缺陷。
  • 我应该切换订单吗?我尝试了几种不同的方式,但是当我的目标是获得所有三个时,我从没有结果变为只有一个结果.. 并且 if 语句是制作 2 x 6 个零或数字块。它是艺术性的,所以它不会选择可以被 3 整除的 I 和 7 整除的随机整数。
  • 切换顺序是什么?你需要解释你想要完成什么

标签: list loops python-3.x while-loop


【解决方案1】:

很难准确说出您要问什么,但我认为您只是想在矩阵中的某个位置随机分配数字 3、4 和 5,条件是 i 不能被 3 和 j 整除不能被7 整除。如果这就是你想要的,那么应该这样做:

import random

array = [ [ 0 for j in range(28) ] for i in range(9) ]

for n in [3, 4, 5]:
    while True:
        i = random.randint(1, 9)
        if i % 3 != 0:
            break
    while True:
        j = random.randint(1, 28)
        if j % 7 != 0:
            break
    array[i][j] = n

print('\n'.join(str(a) for a in array))

那么让我们来谈谈如何得到这个答案。首先,我们想要一种生成随机数的方法直到条件发生。在大多数语言中,这将涉及 do-while 循环,但 Python 没有这些。但是,我们可以只使用 while 循环来制作与 do-while 循环等效的东西:

# This says to run this loop *forever*
while True:
   # do something here
   pass # This means "do nothing" in Python
   if condition:
       # This says if the previously mentioned condition
       # is True then we will stop executing the
       # currently containing loop.
       break

所以我刚刚展示的这个结构是一个构建块,您可以使用它来创建一个循环,该循环一直运行直到满足条件。

让我们看看它如何适合您的原始示例。我们想要一个在 [1, 9) 范围内不能被 3 整除的随机数。random.randint 函数将提供该范围内的随机数,但它不 保证它不能被 3 整除。所以我们需要自己强制执行该约束。一种方法是在不满足约束时简单地生成一个新数字。

所以现在我们可以使用前面讨论的循环构造来构建一个循环,该循环一直运行到我们有一个不能被 3 整除的数字:

while True:
    i = random.randint(1, 9)
    if i % 3 != 0:
        break

我相信您可以在前一个循环中进行逻辑替换,看看它如何适合另一个示例。

所以现在我们可以多谈谈您在原始代码中哪里出了问题,以及如何防止以后再犯同样的错误。

我们先说这一行:

while 3 not in array:

首先,在开发代码时,最好在 Python 交互式解释器提示符下进行尝试。当您第一次尝试该语言的某个功能时,这一点尤其更是如此。正如我在评论中所展示的,while 循环中的条件是always False。如果您在解释器中尝试一下,原因就很清楚了:

>>> 3 in [[3],[3],[3]]
False

in 运算符仅在列表中查看一层。此外,在交互式测试时从小处着手总是一个好主意。请注意,我使用的是一个只有 3 个元素的列表,其中每个嵌套列表仅包含 1 个元素,而不是原始示例中的 9 元素列表,其中嵌套列表包含 28 个元素。

现在,我们可以用来使循环条件随时间变化的另一种方法是创建 in 运算符的“递归”版本。或者,我们可以硬编码它以期望包含列表的列表。我将采用第二种方法,因为它更简单,不知道您是否已经熟悉递归,这已经是一个很长的解释了。

def contains2d(outer, element):
    """Expects a 2D-array-like list. Returns True if any of the inner
    sub-lists within the outer list contain element.
    """
    return any([e in inner for inner in outer])

如果我们尝试这个函数,我们会发现它的行为与您最初期望的 in 运算符的行为一样:

>>> contains2d([[3],[3],[3]], 3)
True
>>> contains2d([[0],[0],[3]], 3)
True
>>> contains2d([[0],[0],[0]], 3)
False

那么现在让我们谈谈您对continue 关键字的误解。 while 循环自动重复。您不需要在 while 循环内使用 continue 来重复它。 continue 关键字仅用于跳过循环体的其余部分。如果您有一些不想进行正常循环处理的特殊情况,通常会使用此方法。下面是一个合理的例子,说明如何使用continue

>>> for i in range(10):
...     if i % 3 == 0:
...         continue
...     print(i)
... 
1
2
4
5
7
8

请注意,将continue 放在循环末尾确实没有任何意义:

while True:
    print("This loop runs forever!")
    # The following continue is useless
    continue

好的,既然我们有了contains2d,我们就可以开始考虑我们想要什么了。在您的示例中,您说您希望 array 变量在循环结束时包含 3,4 和 5。再一次,让我们从小事做起,看看在所需的情况下条件是否为真。我们从前面知道contains2d([[0],[0],[3]], 3) == True,所以这是一个不充分的循环终止标准。请记住,我们希望循环仅在满足所有条件时才为真。这意味着我们需要使用and 运算符

>>> contains2d([[0],[0],[3]], 3) and contains2d([[0],[0],[3]], 4) and contains2d([[0],[0],[3]], 5)
False
>>> contains2d([[4],[0],[3]], 3) and contains2d([[4],[0],[3]], 4) and contains2d([[4],[0],[3]], 5)
False
>>> contains2d([[4],[5],[3]], 3) and contains2d([[4],[5],[3]], 4) and contains2d([[4],[5],[3]], 5)
True

请注意,这种情况非常丑陋且难以编写。理想情况下,我们可能想重构它。一种方法是使用内置的all 函数。我会让你自己试验一下,但最终结果如下:

>>> all([contains2d([[4],[5],[3]], e) for e in [3,4,5]])
True

这要短得多,也更清楚。所以,继续前进,只要条件是 not True:

,我们就想运行这个循环
while not all([contains2d(array, e) for e in [3,4,5]]):
    # ...

接下来的两行其实还好,不过我会按照PEP-8来格式化:

while not all([contains2d(array, e) for e in [3,4,5]]):
    i = random.randint(1, 9)
    j = random.randint(1, 28)
    # ...

如果我们用 contains2d 函数替换 in 条件,那么我们几乎可以找到一个可行的解决方案:

while not all([contains2d(array, e) for e in [3,4,5]]):
    i = random.randint(1, 9)
    j = random.randint(1, 28)
    if i % 3 != 0 and j % 7 != 0:
        if contains2d(array, 5):
            # ...
        elif contains2d(array, 3):
            # ...
        else:
            # ...

if 条件内的分配也非常好。但是,当您在循环中有 return 时,它将退出循环并退出整个包含函数。在此代码中,您实际上没有包含函数,因此这只会结束您的程序。这不是你想在这里做的,所以让我们放弃所有这些return 声明:

while not all([contains2d(array, e) for e in [3,4,5]]):
    i = random.randint(1, 9)
    j = random.randint(1, 28)
    if i % 3 != 0 and j % 7 != 0:
        if contains2d(array, 5):
            array[i][j] = 3
        elif contains2d(array, 3):
            array[i][j] = 4
        else:
            array[i][j] = 5

这个程序非常接近是正确的。然而,最里面的if 块使用了一些奇怪的逻辑。如果你使用我的初始化和输出运行这个程序,你会看到这样的:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 5, 4]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]
[0, 4, 4, 4, 4, 4, 4, 0, 3, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4]

实际上我花了一分钟才弄清楚出了什么问题,但关键的观察是,第一次通过循环获得一对成功的 ij,你会采取else 分支在嵌套列表中随机放置一个 5。下一次成功进入if 时,您将采用第一个分支,该分支在嵌套列表中随机放置一个 3。然而,事情就是这样横向发展的。在下一次成功的迭代中,您仍将采用第一个分支,因此您将随机放置 another 3 到嵌套列表中。您将继续这样做多次迭代。最终,您将覆盖先前写入的 5。此时,第一个条件将不再成立,而是您将开始采用第二个分支,该分支随机将 4 放入嵌套列表中。由于嵌套列表中不再存在 5,因此将继续采用此分支。最终,您将在嵌套列表中用 4s 覆盖所有先前写入的 3s。此时,第二个分支条件将不再成立,您将再次登陆 else 分支,将 5 写入嵌套列表。最后,第一个条件将再次成立,所以在下一次成功的迭代中,你将再次写一个 3,只要你不走运,那将错过 5,你现在已经满足终止条件对于外部 while 循环,您会得到一堆 4,只有一个 3 和一个 5。

也许这就是您想要做的,但从您的问题看来并非如此,所以我们假设您真的只想要嵌套列表中存在的每个数字之一。如果是这样的话,我们可以很容易地纠正之前的程序。我们只需要修复每个if 案例的条件。我们只想在 5 出现时采用第一个分支,但 3 出现。因此,这给了我们if contains2d(array, 5) and not contains2d(array, 3):。此外,如果 5 和 3 已经存在,我们只想采用第二个分支。因此,这给了我们elif contains2d(array, 5) and contains2d(array, 3):。最后,如果不存在 5,我们只想采用最后一个分支。因此,我们必须将else 更改为另一个elif,从而为我们提供elif not contains2d(array, 5):。把这一切放在一起给我们:

array = [ [ 0 for j in range(28) ] for i in range(9) ]
while not all([contains2d(array, e) for e in [3,4,5]]):
    i = random.randint(1, 9)
    j = random.randint(1, 28)
    if i % 3 != 0 and j % 7 != 0:
        if contains2d(array, 5) and not contains2d(array, 3):
            array[i][j] = 3
        elif contains2d(array, 5) and contains2d(array, 3):
            array[i][j] = 4
        elif not contains2d(array, 5):
            array[i][j] = 5
print('\n'.join(str(a) for a in array))

这实际上就像我原来的答案一样。但是,它并不是很令人满意,因为if 块内的逻辑相当复杂。实际上,必须发生一系列非常严格的事件。每当您想到一系列事物时,您都应该想到一个列表。在这种情况下,顺序是这样的,我们分配 5,然后我们分配 3,最后我们分配 4。这可以表示为这个列表:[5, 3, 4]。如果我们重新排序一下,我们可以得到以下程序:

array = [ [ 0 for j in range(28) ] for i in range(9) ]
for n in [5, 3, 4]:
    while not contains2d(array, n):
        i = random.randint(1, 9)
        j = random.randint(1, 28)
        if i % 3 != 0 and j % 7 != 0:
            array[i][j] = n
print('\n'.join(str(a) for a in array))

这个特定的程序确实有一个缺陷,即后面的值之一可能会覆盖前面的值之一,因此如果你的随机数碰巧发生冲突,所有出现的数字的后置条件可能不成立。事实上,我最初的回答也有同样的问题。我将把它作为一个练习留给你来解决。

【讨论】:

  • 感谢 b4hand,听起来它很好地解决了我的困境.. 可悲的是,我仍然没有征服学习 while 循环.. 但该修复不再是问题....再次,谢谢
  • 好的,既然你已经确认这是你想要的。我会继续解释如何到达那里。如果您想要完全不同的东西,我不会花很多时间解释它。
  • 非常感谢您的解释,也感谢“留下一些”......我总是在 python 中学习一些东西,并给我留下空间,但阴谋是完美的!
猜你喜欢
  • 2011-08-09
  • 1970-01-01
  • 2022-01-16
  • 2017-05-03
  • 1970-01-01
  • 2019-02-17
  • 2012-01-02
  • 2013-09-28
  • 2012-05-17
相关资源
最近更新 更多