在下面的代码中,c
是一个常数序列(Countdown
的实例),它可以迭代其元素并在满足条件时中断,并且可以从头开始迭代。
但是当我直接调用c.next()
时,出现编译器错误:不能在不可变值上使用变异成员。
所以,我有两个问题:
c.next()
,为什么首先要遍历所有元素?是不是在内部使用next()
方法来遍历它们?for-in
循环中,为什么不从第一次迭代终止的1
开始计数,而是从头一个3
开始计数?
struct Countdown: Sequence, IteratorProtocol {
// internal state
var count: Int
// IteratorProtocol requirement
mutating func next() -> Int? {
if count == 0 { return nil } else {
defer { count -= 1 }
return count
}
}
}
// a constant sequence
let c = Countdown(count: 3)
// can iterate and break
for i in c {
print(i) // 3, 2
if i == 2 { break }
}
// iterate again from start (not from 1, why?)
for i in c { print(i) } // 3, 2, 1
// ⛔️ Error: cannot use mutating member on immutable value.
// `c` is a `let` constant.
c.next()
您无法呼叫next
,因为next
为mutating
。next
更改迭代器的状态,以使其“移近”到末尾。
for
循环不会直接调用next
。它创建c
(var
)的mutable副本,并在其上调用next
:
// The for loop basically does this:
var copy = c // creates a copy
var element = copy.next()
element = copy.next()
element = copy.next()
...
第二个for循环从头开始的原因就是因为这个。 for
循环实际上并不会改变您要迭代的事物的状态。他们创建一个副本,使用该副本,然后将其丢弃。
避免这种复制行为的一种方法是将Countdown
设为类:
class Countdown: Sequence, IteratorProtocol {
// internal state
var count: Int
// IteratorProtocol requirement
func next() -> Int? {
if count == 0 { return nil } else {
defer { count -= 1 }
return count
}
}
init(from n: Int){
count = n
}
}
因为每个for
循环都通过调用序列的makeIterator()
函数来创建新的迭代器,该函数由标准库在Sequence
的条件表达式中定义,但仅当符合条件的序列也符合[ C0],就像您一样。
[IteratorProtcol
循环的除糖揭示了问题:
for
有两个单独的迭代器,每个let c = Countdown(from: 3)
var iterator1 = c.makeIterator()
while let i = iterator1.next() {
print(i) // 3, 2
if i == 2 { break }
}
var iterator2 = c.makeIterator()
while let i = iterator2.next() { print(i) } // 3, 2, 1
循环一个。每个人都源自for
的副本(从未更改过),从而“重新开始”。