Wie iteriere ich schnell in umgekehrter Reihenfolge für die Schleife?

186

Wenn ich die for-Schleife in Playground verwende, hat alles gut funktioniert, bis ich den ersten Parameter der for-Schleife auf den höchsten Wert geändert habe. (in absteigender Reihenfolge iteriert)

Ist das ein Fehler? Hatte es sonst noch jemand?

for index in 510..509
{
    var a = 10
}

Der Zähler, der die Anzahl der Iterationen anzeigt, die ausgeführt werden sollen, tickt weiter ...

Geben Sie hier die Bildbeschreibung ein

Krishnan
quelle
3
Mögliches Duplikat von Reverse Range in Swift
Matt Gibson
1
Solch eine brillante Entscheidung, Schleifen im C-Stil fallen zu lassen. Bring sie zurück
Joel Teply
Siehe meine Antwort mit Swift 5 , die bis zu 4 verschiedene Möglichkeiten zum Iterieren in umgekehrter Reihenfolge zeigt.
Imanou Petit

Antworten:

229

Xcode 6 Beta 4 hat zwei Funktionen hinzugefügt, um Bereiche mit einem anderen Schritt als einem zu iterieren: Dies stride(from: to: by:)wird mit exklusiven Bereichen und stride(from: through: by:)mit inklusiven Bereichen verwendet.

Um einen Bereich in umgekehrter Reihenfolge zu durchlaufen, können sie wie folgt verwendet werden:

for index in stride(from: 5, to: 1, by: -1) {
    print(index)
}
//prints 5, 4, 3, 2

for index in stride(from: 5, through: 1, by: -1) {
    print(index)
}
//prints 5, 4, 3, 2, 1

Beachten Sie, dass keine dieser RangeFunktionen eine Mitgliedsfunktion ist. Dies sind globale Funktionen, die entweder eine StrideTooder eine StrideThroughStruktur zurückgeben, die sich von der RangeStruktur unterscheiden.

In einer früheren Version dieser Antwort wurde die by()Member-Funktion der RangeStruktur verwendet, die in Beta 4 entfernt wurde. Wenn Sie sehen möchten, wie dies funktioniert, überprüfen Sie den Bearbeitungsverlauf.

Cezar
quelle
26
Mit Swift 2.0 ist es jetzt: 5.stride(to: 1, by: -1)oder5.stride(through: 1, by: -1)
Binarian
6
Mit Swift 3.0 sind wir zurück, um als freie Funktion voranzukommen.
Cezar
@Shaun es ist eine vernünftige und sichere Wahl. Ich kann Ihnen nicht oft sagen, dass R-Programmierer von ihrem :Operator ausgelöst werden, der implizit nach unten iteriert, was dazu führt, dass ihre Schleifen ausgeführt werden, wenn sie dachten, dass sie überspringen würden. Es ist sehr praktisch für die interaktive Verwendung, aber in Skripten furchtbar fehleranfällig, bis zu dem Punkt, an dem Styleguides davon abraten, es zu verwenden .
Davor Cubranic
212

Wenden Sie die Umkehrfunktion auf den Bereich an, um rückwärts zu iterieren:

Für Swift 1.2 und früher:

// Print 10 through 1
for i in reverse(1...10) {
    println(i)
}

Es funktioniert auch mit halboffenen Bereichen:

// Print 9 through 1
for i in reverse(1..<10) {
    println(i)
}

Hinweis: reverse(1...10)Erstellt ein Array vom Typ [Int]. Dies ist zwar für kleine Bereiche in Ordnung, es ist jedoch ratsam, es lazywie unten gezeigt zu verwenden oder die akzeptierte strideAntwort zu berücksichtigen , wenn Ihr Bereich groß ist.


Verwenden Sie lazyzusammen mit, um das Erstellen eines großen Arrays zu vermeiden reverse(). Der folgende Test läuft effizient auf einem Spielplatz und zeigt, dass kein Array mit einer Billion Ints erstellt wird!

Prüfung:

var count = 0
for i in lazy(1...1_000_000_000_000).reverse() {
    if ++count > 5 {
        break
    }
    println(i)
}

Für Swift 2.0 in Xcode 7:

for i in (1...10).reverse() {
    print(i)
}

Beachten Sie, dass Swift 2.0 (1...1_000_000_000_000).reverse()vom Typ ist ReverseRandomAccessCollection<(Range<Int>)>, sodass dies einwandfrei funktioniert:

var count = 0
for i in (1...1_000_000_000_000).reverse() {
    count += 1
    if count > 5 {
        break
    }
    print(i)
}

For Swift 3.0 reverse() wurde umbenannt in reversed():

for i in (1...10).reversed() {
    print(i) // prints 10 through 1
}
vacawama
quelle
3
reversedkann die Sammlung jedoch kopieren. Zumindest verstehe ich so die Dokumentation dazu Data.
Raphael
96

Aktualisiert für Swift 3

Die Antwort unten ist eine Zusammenfassung der verfügbaren Optionen. Wählen Sie diejenige, die Ihren Anforderungen am besten entspricht.

reversed: Zahlen in einem Bereich

Nach vorne

for index in 0..<5 {
    print(index)
}

// 0
// 1
// 2
// 3
// 4

Rückwärts

for index in (0..<5).reversed() {
    print(index)
}

// 4
// 3
// 2
// 1
// 0

reversed: Elemente in SequenceType

let animals = ["horse", "cow", "camel", "sheep", "goat"]

Nach vorne

for animal in animals {
    print(animal)
}

// horse
// cow
// camel
// sheep
// goat

Rückwärts

for animal in animals.reversed() {
    print(animal)
}

// goat
// sheep
// camel
// cow
// horse

reversed: Elemente mit einem Index

Manchmal wird ein Index benötigt, wenn eine Sammlung durchlaufen wird. Dafür können Sie verwenden enumerate(), was ein Tupel zurückgibt. Das erste Element des Tupels ist der Index und das zweite Element ist das Objekt.

let animals = ["horse", "cow", "camel", "sheep", "goat"]

Nach vorne

for (index, animal) in animals.enumerated() {
    print("\(index), \(animal)")
}

// 0, horse
// 1, cow
// 2, camel
// 3, sheep
// 4, goat

Rückwärts

for (index, animal) in animals.enumerated().reversed()  {
    print("\(index), \(animal)")
}

// 4, goat
// 3, sheep
// 2, camel
// 1, cow
// 0, horse

Beachten Sie, dass Sie, wie Ben Lachman in seiner Antwort feststellte , dies wahrscheinlich .enumerated().reversed()eher tun möchten als .reversed().enumerated()(was die Indexzahlen erhöhen würde).

Schritt: Zahlen

Schritt ist eine Möglichkeit, ohne Verwendung eines Bereichs zu iterieren. Es gibt zwei Formen. Die Kommentare am Ende des Codes zeigen, wie die Bereichsversion aussehen würde (vorausgesetzt, die Inkrementgröße ist 1).

startIndex.stride(to: endIndex, by: incrementSize)      // startIndex..<endIndex
startIndex.stride(through: endIndex, by: incrementSize) // startIndex...endIndex

Nach vorne

for index in stride(from: 0, to: 5, by: 1) {
    print(index)
}

// 0
// 1
// 2
// 3
// 4

Rückwärts

Durch Ändern der Inkrementgröße auf -1können Sie rückwärts gehen.

for index in stride(from: 4, through: 0, by: -1) {
    print(index)
}

// 4
// 3
// 2
// 1
// 0

Beachten Sie den tound throughUnterschied.

Schritt: Elemente von SequenceType

In Schritten von 2 vorwärts

let animals = ["horse", "cow", "camel", "sheep", "goat"]

Ich verwende 2in diesem Beispiel nur, um eine andere Möglichkeit zu zeigen.

for index in stride(from: 0, to: 5, by: 2) {
    print("\(index), \(animals[index])")
}

// 0, horse
// 2, camel
// 4, goat

Rückwärts

for index in stride(from: 4, through: 0, by: -1) {
    print("\(index), \(animals[index])")
}

// 4, goat
// 3, sheep 
// 2, camel
// 1, cow  
// 0, horse 

Anmerkungen

Suragch
quelle
52

Swift 4 ab

for i in stride(from: 5, to: 0, by: -1) {
    print(i)
}
//prints 5, 4, 3, 2, 1

for i in stride(from: 5, through: 0, by: -1) {
    print(i)
}
//prints 5, 4, 3, 2, 1, 0
Irfan
quelle
28

Mit Swift 5 können Sie je nach Bedarf eines der vier folgenden Playground-Codebeispiele auswählen , um Ihr Problem zu lösen.


# 1. Mit ClosedRange reversed()Methode

ClosedRangehat eine Methode namens reversed(). reversed()Methode hat die folgende Deklaration:

func reversed() -> ReversedCollection<ClosedRange<Bound>>

Gibt eine Ansicht zurück, in der die Elemente der Sammlung in umgekehrter Reihenfolge dargestellt werden.

Verwendung:

let reversedCollection = (0 ... 5).reversed()

for index in reversedCollection {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

Alternativ können Sie folgende Range reversed()Methode verwenden:

let reversedCollection = (0 ..< 6).reversed()

for index in reversedCollection {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

# 2. Mit sequence(first:next:)Funktion

Die Swift Standard Library bietet eine Funktion namens sequence(first:next:). sequence(first:next:)hat die folgende Erklärung:

func sequence<T>(first: T, next: @escaping (T) -> T?) -> UnfoldFirstSequence<T>

Gibt eine Sequenz zurück, die aus firstund wiederholte verzögerte Anwendungen von gebildet wurde next.

Verwendung:

let unfoldSequence = sequence(first: 5, next: {
    $0 > 0 ? $0 - 1 : nil
})

for index in unfoldSequence {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

#3. Mit stride(from:through:by:)Funktion

Die Swift Standard Library bietet eine Funktion namens stride(from:through:by:). stride(from:through:by:)hat die folgende Erklärung:

func stride<T>(from start: T, through end: T, by stride: T.Stride) -> StrideThrough<T> where T : Strideable

Gibt eine Sequenz von einem Startwert zu einem Endwert zurück und enthält möglicherweise einen Endwert, wobei der angegebene Betrag schrittweise überschritten wird.

Verwendung:

let sequence = stride(from: 5, through: 0, by: -1)

for index in sequence {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

Alternativ können Sie Folgendes verwenden stride(from:to:by:):

let sequence = stride(from: 5, to: -1, by: -1)

for index in sequence {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

# 4. Mit AnyIterator init(_:)initializer

AnyIteratorhat einen Initialisierer aufgerufen init(_:). init(_:)hat die folgende Erklärung:

init(_ body: @escaping () -> AnyIterator<Element>.Element?)

Erstellt einen Iterator, der den angegebenen Abschluss in seine next()Methode einschließt.

Verwendung:

var index = 5

guard index >= 0 else { fatalError("index must be positive or equal to zero") }

let iterator = AnyIterator({ () -> Int? in
    defer { index = index - 1 }
    return index >= 0 ? index : nil
})

for index in iterator {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

Bei Bedarf können Sie den vorherigen Code umgestalten, indem Sie eine Erweiterungsmethode für IntIhren Iterator erstellen und diesen einschließen:

extension Int {

    func iterateDownTo(_ endIndex: Int) -> AnyIterator<Int> {
        var index = self
        guard index >= endIndex else { fatalError("self must be greater than or equal to endIndex") }

        let iterator = AnyIterator { () -> Int? in
            defer { index = index - 1 }
            return index >= endIndex ? index : nil
        }
        return iterator
    }

}

let iterator = 5.iterateDownTo(0)

for index in iterator {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/
Imanou Petit
quelle
Sequenz (erste: 5, nächste: {($ 0> 0)? ($ 0 - 1): nil}) // ist einfacher als ($ 0 - 1> = 0)
SirEnder
27

Für Swift 2.0 und höher sollten Sie eine Bereichssammlung umgekehrt anwenden

for i in (0 ..< 10).reverse() {
  // process
}

Es wurde in Swift 3.0 in .reversed () umbenannt

Andrew Luca
quelle
9

In Swift 4 und letzterem

    let count = 50//For example
    for i in (1...count).reversed() {
        print(i)
    }
Rohit Sisodia
quelle
5

Swift 4.0

for i in stride(from: 5, to: 0, by: -1) {
    print(i) // 5,4,3,2,1
}

Wenn Sie den toWert einschließen möchten :

for i in stride(from: 5, through: 0, by: -1) {
    print(i) // 5,4,3,2,1,0
}
William Hu
quelle
3

Wenn man ein Array ( Arrayoder allgemeiner eines SequenceType) in umgekehrter Reihenfolge durchlaufen möchte . Sie haben einige zusätzliche Optionen.

Zuerst können Sie reverse()das Array wie gewohnt durchlaufen und es durchlaufen. Ich bevorzuge es jedoch, enumerate()viel Zeit zu verwenden, da es ein Tupel ausgibt, das das Objekt und seinen Index enthält.

Hier ist zu beachten, dass es wichtig ist, diese in der richtigen Reihenfolge aufzurufen:

for (index, element) in array.enumerate().reverse()

liefert Indizes in absteigender Reihenfolge (was ich allgemein erwarte). wohingegen:

for (index, element) in array.reverse().enumerate()(das ist eine engere Übereinstimmung mit NSArray reverseEnumerator)

Geht das Array rückwärts, gibt jedoch aufsteigende Indizes aus.

Ben Lachman
quelle
3

wie für Swift 2.2, Xcode 7.3 (10. Juni 2016):

for (index,number) in (0...10).enumerate() {
    print("index \(index) , number \(number)")
}

for (index,number) in (0...10).reverse().enumerate() {
    print("index \(index) , number \(number)")
}

Ausgabe :

index 0 , number 0
index 1 , number 1
index 2 , number 2
index 3 , number 3
index 4 , number 4
index 5 , number 5
index 6 , number 6
index 7 , number 7
index 8 , number 8
index 9 , number 9
index 10 , number 10


index 0 , number 10
index 1 , number 9
index 2 , number 8
index 3 , number 7
index 4 , number 6
index 5 , number 5
index 6 , number 4
index 7 , number 3
index 8 , number 2
index 9 , number 1
index 10 , number 0
PeiweiChen
quelle
2

Sie können stattdessen die C-Style- whileSchleife verwenden. Dies funktioniert in Swift 3 einwandfrei:

var i = 5 
while i > 0 { 
    print(i)
    i -= 1
}
Nitin Nain
quelle
2

Sie können die reverse () -Methode verwenden, um Werte einfach umzukehren.

var i:Int
for i in 1..10.reversed() {
    print(i)
}

Die Methode reverse () kehrt die Werte um.

SIDHARTH PU
quelle
Warum würden Sie eine Antwort hinzufügen, die mit der bereits vorhandenen identisch ist? (ZB @
Rohits
1
var sum1 = 0
for i in 0...100{
    sum1 += i
}
print (sum1)

for i in (10...100).reverse(){
    sum1 /= i
}
print(sum1)
Abo3atef
quelle