2015-01-31 17 views
6

Wenn normalerweise mit einer for-in-Schleife, der Zähler (in diesem Fall number) eine Konstante in jeder Iteration:Zähler als Variable in für in-Schleife

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

Das bedeutet, ich nicht number ändern kann in der Schleife:

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

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

gibt es eine Möglichkeit number als Variable zu deklarieren, ohne sie vor der Schleife zu deklarieren, oder ein normal-Schleife (mit Initialisierung, Bedingung und Inkrement)?

+1

das Ändern Iterator eines 'for' wird normalerweise als schlechter Code angesehen. 'for in' hat keinen Iterator,' number' ist kein Iterator. Sein Wert beeinflusst die Schleife in keiner Weise. – Sulthan

Antwort

12

Um zu verstehen, warum i nicht veränderbar sein kann, muss man wissen, was for…in abgekürzt ist. for i in 0..<10 wird vom Compiler die folgenden erweitert:

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

Jedes Mal, um die Schleife, i eine frisch deklarierte Variable ist, wird der Wert von der nächsten Folge Abwickeln next am Generator aufrufen.

Nun, die whilekann wie folgt geschrieben werden:

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

aber natürlich wäre es nicht helfen - g.next() ist immer noch ein 5 beim nächsten Mal um die Schleife zu erzeugen gehen. Der Zuwachs im Körper war sinnlos.

Vermutlich aus diesem Grund unterstützt for…in nicht die gleiche var Syntax für die Deklaration seiner Schleife Zähler - es wäre sehr verwirrend, wenn Sie nicht wissen, wie es funktioniert.

(im Gegensatz zu where, wo Sie sehen können, was passiert - die var Funktionalität ist gelegentlich nützlich, ähnlich wie func f(var i) sein kann).

Wenn das, was Sie wollen, ist sicher Iterationen der Schleife zu überspringen, Ihre bessere Wette (ohne for oder while bis C-Stil Zuflucht) ist, einen Generator zu verwenden, die die entsprechenden Werte überspringt:

// 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

Sie geben einfach gute Antworten! –

+0

Swift 3 erlaubt var in für ... in Schleifen. Es wird jedoch bei jeder Runde überschrieben. – Etan

2

Die Antwort ist "Nein", und das ist eine gute Sache. Ansonsten wären ein grob verwirrend Verhalten wie das möglich:

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

die Verwirrung von jemandem an der Nummer 5000 in der Ausgabe einer Schleife Suche Stellen Sie sich vor, die angeblich auf einen Bereich von 1 zwar einschließlich 10 gebunden ist.

Außerdem, was würde Swift als nächsten Wert von 5000 wählen? Sollte es aufhören? Soll es bis zur nächsten Nummer im Bereich vor der Aufgabe weitergehen? Sollte es eine Ausnahme außerhalb der Reichweite werfen? Alle drei Entscheidungen haben eine gewisse Gültigkeit, daher gibt es keinen eindeutigen Gewinner.

Um solche Situationen zu vermeiden, haben Swift-Konstrukteure Loop-Variablen in Range Loops unveränderlich gemacht.

+0

Es ist wirklich hilfreich, über "für in" als eine völlig andere Struktur zu denken, die eine gewöhnliche "for" -Schleife ist. 'Nummer' ist kein Iterator, es ist ein Parameter einer Funktion/eines Blocks, der in jeder Iteration aufgerufen wird. – Sulthan