2015-01-31 34 views
6

przypadku zwykle stosując na w pętli, licznik (w tym przypadku number) jest stała w każdej iteracji:licznikiem zmiennej, dla w pętle

for number in 1...10 { 
    // do something 
} 

Oznacza to, że nie można zmieniać number w pętli:

for number in 1...10 { 
    if number == 5 { 
     ++number 
    } 
} 

// doesn't compile, since the prefix operator '++' can't be performed on the constant 'number' 

Czy istnieje sposób deklarowania number jako zmiennej, bez deklarowania przed pętlą lub za pomocą normalnej pętli for (z inicjalizacji, stanu i przyrostu)?

+1

modyfikowania iterator 'for' jest zwykle uważany za zły kod. 'for in' nie ma iteratora,' number' nie jest iteratorem. Jego wartość w żaden sposób nie wpływa na pętlę. – Sulthan

Odpowiedz

12

Aby zrozumieć, dlaczego i nie można zmienić, trzeba wiedzieć, co jest skrócone do for…in. for i in 0..<10 jest rozszerzany przez kompilator do następujących:

var g = (0..<10).generate() 
while let i = g.next() { 
    // use i 
} 

każdym razem wokół pętli i jest świeżo zadeklarowana zmienna, wartość rozpakowaniu następny wynik z wywołaniem next na generatorze.

teraz, że whilemoże być napisane tak:

while var i = g.next() { 
    // here you _can_ increment i: 
    if i == 5 { ++i } 
} 

ale oczywiście to nie pomoże - g.next() nadal będzie generować 5 następnym razem wokół pętli. Wzrost w ciele był bezcelowy.

Prawdopodobnie z tego powodu nie obsługuje tej samej składni var dla deklarowania licznika pętli - byłoby bardzo mylące, gdybyś nie wiedział, jak to działa.

(w przeciwieństwie do where, gdzie można zobaczyć, co się dzieje - funkcjonalność var jest czasami przydatna, podobnie jak może być func f(var i)).

Jeśli to, co chcesz, aby pominąć pewne iteracji pętli, twój lepiej zakładu (bez uciekania się do C-stylu for lub while) jest użycie generatora że pomija istotne wartości:

// iterate over every other integer 
for i in 0.stride(to: 10, by: 2) { print(i) } 

// skip a specific number 
for i in (0..<10).filter({ $0 != 5 }) { print(i) } 

let a = ["one","two","three","four"] 

// ok so this one’s a bit convoluted... 
let everyOther = a.enumerate().filter { $0.0 % 2 == 0 }.map { $0.1 }.lazy 

for s in everyOther { 
    print(s) 
} 
+0

Po prostu dajesz świetne odpowiedzi! –

+0

Swift 3 pozwala na zmianę dla ... w pętlach. Jednak jest on nadpisywany w każdej turze. – Etan

2

Odpowiedź brzmi "nie", a to dobrze. W przeciwnym razie, rażąco mylące zachowanie jak to będzie możliwe:

for number in 1...10 { 
    if number == 5 { 
     // This does not work 
     number = 5000 
    } 
    println(number) 
} 

Wyobraź zamieszanie kogoś patrząc na numer 5000 w wyjściu z pętli, która jest rzekomo związanego z zakresu 1 chociaż 10 włącznie.

Co ponadto wybrać Swift jako następną wartość 5000? Czy powinien się zatrzymać? Czy powinien przejść do następnej liczby w zakresie przed zleceniem? Czy powinien rzucić wyjątek na zlecenie poza zakresem? Wszystkie trzy opcje mają dla nich pewną wartość, więc nie ma wyraźnego zwycięzcy.

Aby uniknąć takich sytuacji, projektanci Swift wykonali zmienne pętli w pętlach zasięgu niezmiennie.

+0

To naprawdę pomocne, aby pomyśleć o 'for in' jako o zupełnie innej strukturze niż zwykła pętla' for'. 'liczba' nie jest iteratorem, jest to parametr funkcji/bloku, który jest wywoływany w każdej iteracji. – Sulthan