【问题标题】:Why do some programming languages restrict you from editing the array you're looping through?为什么某些编程语言会限制您编辑正在循环的数组?
【发布时间】:2009-08-20 21:41:27
【问题描述】:

伪代码:

for each x in someArray {
    // possibly add an element to someArray
}

我忘记了在某些语言中引发的异常的名称。

我很想知道为什么有些语言禁止这种用例,而另一些语言则允许。允许的语言是否不安全——容易陷入一些陷阱?或者禁止语言只是过于谨慎,或者可能是懒惰(他们本可以实现语言来优雅地处理这种情况,但根本没有打扰)。

谢谢!

【问题讨论】:

  • 为什么投反对票?这是一个合法的编程语言设计问题。
  • 我想不出一种语言(我用过的)不允许您尝试对其进行编辑。如果您从someArray 中删除x,可能会遇到并发问题。
  • 这需要语言的名称,抛出的异常,甚至是导致问题的代码。简单地说,也许你没有做你认为你在做的事情。除非得到补充,否则我会投票关闭它。
  • 这就是我问题的全部要点;一些编程语言(我认为 C# 就是其中之一)完全阻止了你尝试;即上面的伪代码不会编译。我可以理解对 for 循环意味着什么的更严格的期望(例如“遍历每个数组元素,恰好一次”)。但是,如果我的程序员想要在我的数组中添加或删除元素,我为什么不呢?
  • 好的。我认为 C# 不允许这样做。 Perl 允许这样做。这是我想到的两种语言。

标签: programming-languages loops for-loop


【解决方案1】:

您希望行为是什么?

list = [1,2,3,4]
foreach x in list:
    print x
    if x == 2: list.remove(1)

可能的行为:

list 是一些链表类型的迭代器,其中删除不会影响您当前的迭代器:

[1,2,3,4]

list 是一些数组,你的迭代器通过指针增量迭代:

[1,2,4] 

和之前一样,只是系统尝试缓存迭代次数

[1,2,4,<segfault>]

问题在于实现这个允许 foreach 循环的可枚举/序列接口的不同集合具有不同的行为。

【讨论】:

    【解决方案2】:

    根据语言(或平台,如 .Net),迭代的实现方式可能不同。

    通常,foreach 在数组上创建一个 Iterator 或 Enumerator 对象,该对象在内部保持其关于迭代细节的状态。如果修改数组(通过添加或删除元素),迭代器状态将与数组的新状态不一致。

    .Net 等平台允许您定义自己的枚举器,这些枚举器可能不易添加/删除底层数组的元素。

    迭代时添加/删除元素问题的通用解决方案是收集新列表/集合/数组中的元素,并在枚举完成后添加/删除收集的元素。

    【讨论】:

      【解决方案3】:

      假设您的数组有 10 个元素。您到达第 7 个元素,并在那里决定您需要在数组的前面添加一个新元素。哦哦!该元素不会被迭代! for each 至少对我来说具有对数组的每个元素进行一次且仅一次操作的语义。

      【讨论】:

        【解决方案4】:

        您的伪示例代码会导致无限循环。对于您查看的每个元素,您将添加一个到集合中,因此如果您至少有 1 个元素开始,您将拥有 i(迭代计数器)+ 1 个元素。

        【讨论】:

        • 他说“可能添加”而不是“总是添加”:)
        【解决方案5】:

        数组的元素数量通常是固定的。您可以通过允许发生灵活性的包装对象(例如 List)获得灵活大小的宽度。我怀疑如果他们使用的机制创建了一个全新的数组来允许编辑,那么语言可能会出现问题。

        【讨论】:

          【解决方案6】:

          许多编译语言实现“for”循环,假设迭代次数将在循环启动时计算一次(或者更好的是,编译时)。这意味着如果您在“for i = 1 to x”循环中更改“to”变量的值,它不会改变迭代次数。这样做可以进行大量循环优化,这对于加速数字处理应用程序非常重要。

          如果您不喜欢这种语义,那么您应该改用该语言的“while”构造。

          请注意,在这种世界观中,C 和 C++ 没有适当的“for”循环,只有花哨的“while”循环。

          【讨论】:

            【解决方案7】:

            要实现列表和枚举器来处理此问题,将意味着大量开销。这种开销总是存在的,而且只在极少数情况下有用。

            此外,选择的任何实现并不总是有意义。举个简单的例子,在枚举时在列表中插入一个项目,新项目是否总是包含在枚举中,总是被排除,还是应该取决于项目在列表中的添加位置?如果我在当前位置插入项目,这是否会改变枚举器的 Current 属性的值,是否应该跳过当前当前项目,即下一个项目?

            【讨论】:

              【解决方案8】:

              这只发生在 foreach 块中。使用带有索引值的 for 循环,您将被允许这样做。只需确保向后迭代,这样您就可以删除项目而不会引起问题。

              【讨论】:

                【解决方案9】:

                在我看来,在集合上实现迭代可能有两种情况。

                1. 迭代器遍历为其创建的集合

                2. 迭代器迭代为其创建的集合的副本

                当动态对集合进行更改时,第一个选项应该更新其迭代序列(这可能非常困难甚至不可能可靠地完成)或拒绝这种可能性(抛出异常)。最后一个显然是安全的选择。

                在第二个选项中,可以对原始集合进行更改,而不会影响迭代顺序。但是在迭代中不会看到任何调整,这可能会让用户感到困惑(泄漏抽象)。

                我可以想象语言/库以同等的价值实现这些可能性。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2016-09-03
                  • 2011-02-09
                  • 2018-03-27
                  • 2013-08-13
                  • 2011-09-14
                  • 2019-12-23
                  • 2011-05-15
                  • 2022-11-19
                  相关资源
                  最近更新 更多