swift - последовательность констант может повторяться с использованием for-in l oop, но не может вызывать next () напрямую? - PullRequest
2 голосов
/ 01 апреля 2020

В следующем коде c является константной последовательностью (экземпляр Countdown), она может выполнять итерацию по своим элементам и прерываться при выполнении условия, а также может повторять итерацию с самого начала.

Но когда я вызываю c.next() напрямую, я получаю ошибку компилятора: не может использовать мутирующий член в неизменяемом значении.

Итак, у меня есть два вопроса:

  1. Если я не могу вызвать c.next(), почему он может перебирать все элементы в первую очередь? Разве он не использует внутренний метод next() для их итерации?
  2. Во втором for-in l oop в следующем коде, почему он не считается от 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()

Ответы [ 2 ]

2 голосов
/ 01 апреля 2020

Поскольку каждый for l oop создает новый итератор, вызывая функцию makeIterator() вашей последовательности, которая определяется стандартной библиотекой в ​​условном выражении Sequence, но только в том случае, если последовательность соответствует также соответствует 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 l oop. Каждый из них "запускает fre sh" в силу происхождения из копии c (которая никогда не была видоизменена).

1 голос
/ 01 апреля 2020

Вы не можете позвонить next, потому что next - это mutating. next изменяет состояние итератора , так что он "приближается" к концу.

for l oop не вызывает next напрямую. Он создает изменчивую копию c (var) и вызывает next для этого:

// The for loop basically does this:
var copy = c // creates a copy
var element = copy.next()
element = copy.next()
element = copy.next()
...

Причина, по которой вторая для l oop начинается с начало именно из-за этого. 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
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...