一个常量序列可以使用for-in循环进行迭代,但是不能直接调用next()吗?

问题描述 投票:2回答:2

在下面的代码中,c是一个常数序列(Countdown的实例),它可以迭代其元素并在满足条件时中断,并且可以从头开始迭代。

但是当我直接调用c.next()时,出现编译器错误:不能在不可变值上使用变异成员。

所以,我有两个问题:

  1. 如果无法调用c.next(),为什么首先要遍历所有元素?是不是在内部使用next()方法来遍历它们?
  2. 在下面的代码的第二个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()

swift iterator sequence swift-protocols
2个回答
1
投票

您无法呼叫next,因为nextmutatingnext更改迭代器的状态,以使其“移近”到末尾。

for循环不会直接调用next。它创建cvar)的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
    }
}

2
投票

因为每个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的副本(从未更改过),从而“重新开始”。

© www.soinside.com 2019 - 2024. All rights reserved.