for ... in loop 是否循环遍历序列的副本?

Does for ... in loop loop through a copy of a sequence?

我在 Matt Neuburg 的 iOS 13 Programming Fundamentals with Swift 中遇到了如下陈述:

When you cycle through a sequence with for...in, what you’re actually cycling through is a copy of the sequence. That means it’s safe to mutate the sequence while you’re cycling through it:

作者为上述陈述提供了以下示例:

var s : Set = [1,2,3,4,5]
for i in s {
    if i.isMultiple(of:2) {
        s.remove(i)
    }
} // s is now [1,3,5]

在上面的例子中,我们可以看到原来的数组从[1,2,3,4,5,6]变成了[1,3,5]。这意味着数组本身已更改。因此,for in 循环不是循环遍历原始数组的 copy,而是循环数组 本身 。这似乎与作者的上述陈述相矛盾。

所以,我不确定作者的说法是否属实。有人可以澄清这个问题吗?非常感谢。

这里有一些细微差别。您迭代的数组是一个副本,但您正在修改的数组仍然是原始数组。

在其他情况下,由于索引更改,此代码最终会跳过一些元素。

如果不是这种情况,则考虑以下迭代

// i = 0
// s = [1,2,3,4,5]
// evaluation 1.isMultiple(of: 2)
// element to be removed: none
// i = 1
// s = [1,2,3,4,5]
// evaluation 2.isMultiple(of: 2)
// element to be removed: 2
// Here is where the problems would start, since the number 3 would be skipped.
// i = 2
// s = [1,3,4,5]
// evaluation 4.isMultiple(of: 2)
// element to be removed: 2

由于您正在遍历副本,因此您可以修改 s 并仍然确保考虑所有元素。

措辞乍一看可能有点误导,但 for i in s 循环遍历原始 s 的副本的说法是正确的。如果它不是真的,你会得到一个越界异常或一个意想不到的结果(集合中的一些元素被跳过),因为你会在迭代时改变一个集合。

当您使用 for i in s 创建循环时,会创建 s 的副本,并通过该副本进行迭代。但是,当您在循环体内访问 s 时,您正在访问原始 s 并因此对其进行变异,而不是对循环正在迭代的副本进行变异。