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



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.


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
}

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


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

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

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


9

W Swift 4 i późniejszych

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

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
}

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.


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

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
}

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.


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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.