Jak szybko wykonać iterację pętli w odwrotnej kolejności?

186

Kiedy używam pętli for w Playground, wszystko działało dobrze, dopóki nie zmieniłem pierwszego parametru pętli na najwyższą wartość. (iterowane w kolejności malejącej)

Czy to błąd? Czy ktoś jeszcze to miał?

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

Licznik wyświetlający liczbę iteracji, które zostaną wykonane, wciąż tyka ...

wprowadź opis zdjęcia tutaj

Krishnan
źródło
3
możliwy duplikat wstecznego zasięgu w Swift
Matt Gibson
1
Tak genialna decyzja o upuszczeniu pętli w stylu c. Przynieś je z powrotem
Joel Teply,
Zobacz moją odpowiedź dotyczącą Swift 5, która pokazuje do 4 różnych sposobów iteracji w odwrotnej kolejności.
Imanou Petit

Odpowiedzi:

229

Xcode 6 beta 4 dodał dwie funkcje do iteracji zakresów z krokiem innym niż jeden:, stride(from: to: by:)który jest używany z zakresami wyłącznymi i stride(from: through: by:), który jest używany z zakresami włączającymi.

Aby wykonać iterację zakresu w odwrotnej kolejności, można ich użyć w następujący sposób:

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

Zauważ, że żadna z nich nie jest Rangefunkcją składową . Są to funkcje globalne, które zwracają a StrideTolub StrideThroughstruct, które są zdefiniowane inaczej niż Rangestruct.

Poprzednia wersja tej odpowiedzi korzystała z by()funkcji członka Rangestruktury, która została usunięta w wersji beta 4. Jeśli chcesz zobaczyć, jak to działało, sprawdź historię edycji.

Cezar
źródło
26
W Swift 2.0 jest teraz: 5.stride(to: 1, by: -1)lub5.stride(through: 1, by: -1)
Binarian
6
Dzięki Swift 3.0 wróciliśmy do kroku jako bezpłatna funkcja.
Cezar
@Shaun to rozsądny i bezpieczny wybór. Nie mogę powiedzieć ci często, że programiści R są wyzwalani przez :operatora, pośrednio iterując w dół, powodując, że ich pętle wykonują się, gdy sądzą, że przeskoczy nad nimi. Jest to bardzo wygodne do użytku interaktywnego, ale strasznie podatne na błędy w skryptach, do tego stopnia, że ​​przewodniki stylu zalecają, aby go nie używać .
Davor Cubranic
213

Zastosuj funkcję wstecz do zakresu, aby iterować wstecz:

W przypadku Swift 1.2 i wcześniejszych:

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

Działa również z półotwartymi zakresami:

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

Uwaga: reverse(1...10)tworzy tablicę typu [Int], więc chociaż może to być dobre dla małych zakresów, rozsądnie byłoby użyć, lazyjak pokazano poniżej lub rozważyć zaakceptowaną strideodpowiedź, jeśli twój zakres jest duży.


Aby uniknąć tworzenia dużej tablicy, użyj lazywraz z reverse(). Poniższy test działa skutecznie na placu zabaw, pokazując, że nie tworzy on tablicy z bilionem Ints!

Test:

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

W przypadku Swift 2.0 w Xcode 7:

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

Zauważ, że w Swift 2.0 (1...1_000_000_000_000).reverse()jest typu ReverseRandomAccessCollection<(Range<Int>)>, więc działa dobrze:

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

Dla Swift 3.0 reverse() została zmieniona na reversed():

for i in (1...10).reversed() {
    print(i) // prints 10 through 1
}
vacawama
źródło
3
reversedmoże jednak skopiować kolekcję. Przynajmniej tak rozumiem dokumentację Data.
Raphael,
96

Zaktualizowano dla Swift 3

Odpowiedź poniżej jest podsumowaniem dostępnych opcji. Wybierz ten, który najlepiej odpowiada Twoim potrzebom.

reversed: liczby w zakresie

Naprzód

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

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

Do tyłu

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

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

reversed: elementy w SequenceType

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

Naprzód

for animal in animals {
    print(animal)
}

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

Do tyłu

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

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

reversed: elementy z indeksem

Czasami potrzebny jest indeks podczas iteracji po kolekcji. Do tego możesz użyć enumerate(), który zwraca krotkę. Pierwszym elementem krotki jest indeks, a drugim elementem jest obiekt.

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

Naprzód

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

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

Do tyłu

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

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

Zauważ, że jak zauważył Ben Lachman w swojej odpowiedzi , prawdopodobnie .enumerated().reversed()raczej chcesz to zrobić .reversed().enumerated()(co spowodowałoby wzrost liczby indeksów).

krok: liczby

Krok jest sposobem na iterację bez użycia zakresu. Istnieją dwie formy. Komentarze na końcu kodu pokazują, jaka byłaby wersja zakresu (przy założeniu, że wielkość przyrostu wynosi 1).

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

Naprzód

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

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

Do tyłu

Zmiana wielkości przyrostu na -1pozwala cofnąć się.

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

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

Zauważ, że toi throughróżnicę.

stride: elementy SequenceType

Do przodu o 2

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

Używam 2w tym przykładzie, aby pokazać inną możliwość.

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

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

Do tyłu

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

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

Notatki

Suragch
źródło
52

Swift 4 r

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
źródło
28

W Swift 5, w zależności od potrzeb, możesz wybrać jeden z nich czterech poniższych przykładów kodu placu zabaw , aby rozwiązać problem.


# 1. Za pomocąClosedRange reversed() metody

ClosedRangema metodę o nazwie reversed(). reversed()Metoda ma następującą deklarację:

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

Zwraca widok prezentujący elementy kolekcji w odwrotnej kolejności.

Stosowanie:

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

for index in reversedCollection {
    print(index)
}

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

Alternatywnie możesz użyć Range reversed()metody:

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

for index in reversedCollection {
    print(index)
}

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

# 2. Za pomocąsequence(first:next:) funkcji

Swift Standard Library udostępnia funkcję o nazwie sequence(first:next:). sequence(first:next:)ma następującą deklarację:

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

Zwraca sekwencję utworzoną z firstpowtarzających się leniwych aplikacji next.

Stosowanie:

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. Za pomocąstride(from:through:by:) funkcji

Swift Standard Library udostępnia funkcję o nazwie stride(from:through:by:). stride(from:through:by:)ma następującą deklarację:

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

Zwraca sekwencję od wartości początkowej w kierunku wartości końcowej i ewentualnie włączając wartość końcową, przesuwając się o określoną wartość.

Stosowanie:

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

for index in sequence {
    print(index)
}

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

Alternatywnie możesz użyć 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. Korzystanie z AnyIterator init(_:)inicjalizatora

AnyIteratorma inicjator o nazwie init(_:). init(_:)ma następującą deklarację:

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

Tworzy iterator, który otacza dane zamknięcie swoją next()metodą.

Stosowanie:

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

W razie potrzeby możesz zmienić poprzedni kod, tworząc metodę rozszerzenia Inti owijając w nim swój iterator:

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
źródło
sekwencja (pierwsza: 5, następna: {(0 $> 0)? (0-1): zero}) // jest prostsza niż (
0-1-1
27

W przypadku wersji Swift 2.0 i nowszych należy zastosować odwrotność w zbiorze zakresu

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

Został przemianowany na .reversed () w Swift 3.0

Andrew Luca
źródło
9

W Swift 4 i późniejszych

    let count = 50//For example
    for i in (1...count).reversed() {
        print(i)
    }
Rohit Sisodia
źródło
5

Swift 4.0

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

Jeśli chcesz dołączyć towartość:

for i in stride(from: 5, through: 0, by: -1) {
    print(i) // 5,4,3,2,1,0
}
William Hu
źródło
3

Jeśli ktoś chce iterować przez tablicę ( Arraylub bardziej ogólnie dowolną SequenceType) w odwrotnej kolejności. Masz kilka dodatkowych opcji.

Najpierw możesz reverse()tablicę i przejść przez nią normalnie. Wolę jednak spędzać enumerate()dużo czasu, ponieważ wypisuje krotkę zawierającą obiekt i jego indeks.

Należy tutaj zauważyć, że ważne jest, aby wywoływać je w odpowiedniej kolejności:

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

zwraca indeksy w kolejności malejącej (czego zwykle oczekuję). natomiast:

for (index, element) in array.reverse().enumerate()(co jest bardziej zbliżone do NSArray's reverseEnumerator)

przesuwa tablicę do tyłu, ale wyświetla indeksy rosnące.

Ben Lachman
źródło
3

jak w Swift 2.2, Xcode 7.3 (10, czerwiec 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)")
}

Wynik :

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
źródło
2

Zamiast tego możesz rozważyć użycie whilepętli w stylu C. Działa to dobrze w Swift 3:

var i = 5 
while i > 0 { 
    print(i)
    i -= 1
}
Nitin Nain
źródło
2

Możesz użyć metody reverse (), aby łatwo odwrócić wartości.

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

Metoda reverse () odwraca wartości.

SIDHARTH PU
źródło
Dlaczego miałbyś dodawać odpowiedź identyczną z już istniejącą? (Np. @ Rohit's.)
Davor Cubranic
1
var sum1 = 0
for i in 0...100{
    sum1 += i
}
print (sum1)

for i in (10...100).reverse(){
    sum1 /= i
}
print(sum1)
Abo3atef
źródło