Comment itérer pour la boucle dans l'ordre inverse en Swift?

187

Lorsque j'utilise la boucle for dans Playground, tout fonctionnait bien, jusqu'à ce que je change le premier paramètre de la boucle for pour qu'il soit la valeur la plus élevée. (itéré dans l'ordre décroissant)

Est-ce un bug? Quelqu'un d'autre l'avait-il?

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

Le compteur qui affiche le nombre d'itérations qui seront des exécutions continue de cocher ...

entrez la description de l'image ici

Krishnan
la source
3
duplicata possible de Reverse Range dans Swift
Matt Gibson
1
Une décision si brillante de laisser tomber les boucles de style C. Ramenez-les
Joel Teply
Voir ma réponse avec Swift 5 qui montre jusqu'à 4 façons différentes d'itérer dans l'ordre inverse.
Imanou Petit

Réponses:

230

Xcode 6 beta 4 a ajouté deux fonctions pour itérer sur des plages avec une étape autre que une:, stride(from: to: by:)qui est utilisée avec des plages exclusives et stride(from: through: by:), qui est utilisée avec des plages inclusives.

Pour itérer sur une plage dans l'ordre inverse, ils peuvent être utilisés comme ci-dessous:

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

Notez qu'aucun de ceux-ci n'est une Rangefonction membre. Ce sont des fonctions globales qui renvoient soit a, StrideTosoit une StrideThroughstructure, qui sont définies différemment de la Rangestructure.

Une version précédente de cette réponse utilisait la by()fonction membre de la Rangestructure, qui a été supprimée dans la version bêta 4. Si vous voulez voir comment cela fonctionnait, vérifiez l'historique des modifications.

Cezar
la source
26
Avec Swift 2.0, c'est maintenant: 5.stride(to: 1, by: -1)ou5.stride(through: 1, by: -1)
Binarian
6
Avec Swift 3.0, nous revenons à la foulée en tant que fonction gratuite.
Cezar
@Shaun c'est un choix raisonnable et sûr. Je ne peux pas vous dire que les programmeurs R sont souvent déclenchés par son :opérateur itérant implicitement vers le bas, provoquant l'exécution de leurs boucles alors qu'ils pensaient qu'il les ignorerait. C'est très pratique pour une utilisation interactive, mais terriblement sujet aux erreurs dans les scripts, au point où les guides de style déconseillent de l'utiliser .
Davor Cubranic
213

Appliquez la fonction inverse à la plage pour itérer vers l'arrière:

Pour Swift 1.2 et versions antérieures:

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

Il fonctionne également avec des plages semi-ouvertes:

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

Remarque: reverse(1...10)crée un tableau de type [Int], donc bien que cela puisse convenir pour les petites plages, il serait sage de l'utiliser lazycomme indiqué ci-dessous ou de considérer la strideréponse acceptée si votre plage est grande.


Pour éviter de créer un grand tableau, utilisez lazyavec reverse(). Le test suivant s'exécute efficacement dans un Playground montrant qu'il ne crée pas un tableau avec un billion de Ints!

Tester:

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

Pour Swift 2.0 dans Xcode 7:

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

Notez que dans Swift 2.0, (1...1_000_000_000_000).reverse()est de type ReverseRandomAccessCollection<(Range<Int>)>, donc cela fonctionne bien:

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

Pour Swift 3.0 reverse() a été renommé en reversed():

for i in (1...10).reversed() {
    print(i) // prints 10 through 1
}
vacawama
la source
3
reversedpeut copier la collection, cependant. Au moins, c'est ainsi que je comprends la documentation sur Data.
Raphael
96

Mis à jour pour Swift 3

La réponse ci-dessous est un résumé des options disponibles. Choisissez celui qui correspond le mieux à vos besoins.

reversed: nombres dans une plage

Vers l'avant

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

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

En arrière

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

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

reversed: éléments dans SequenceType

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

Vers l'avant

for animal in animals {
    print(animal)
}

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

En arrière

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

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

reversed: éléments avec un index

Parfois, un index est nécessaire lors de l'itération dans une collection. Pour cela, vous pouvez utiliser enumerate(), qui renvoie un tuple. Le premier élément du tuple est l'index et le deuxième élément est l'objet.

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

Vers l'avant

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

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

En arrière

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

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

Notez que comme Ben Lachman l'a noté dans sa réponse , vous voulez probablement faire .enumerated().reversed()plutôt que .reversed().enumerated()(ce qui ferait augmenter les indices).

foulée: chiffres

Stride est un moyen d'itérer sans utiliser de plage. Il existe deux formes. Les commentaires à la fin du code montrent ce que serait la version de la plage (en supposant que la taille d'incrémentation est de 1).

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

Vers l'avant

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

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

En arrière

Changer la taille d'incrément en -1vous permet de revenir en arrière.

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

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

Notez la différence toet through.

stride: éléments de SequenceType

Avancer par incréments de 2

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

J'utilise 2dans cet exemple juste pour montrer une autre possibilité.

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

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

En arrière

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

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

Remarques

Suragch
la source
52

Swift 4 et plus

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
la source
28

Avec Swift 5, selon vos besoins, vous pouvez choisir l'un des quatre exemples de code Playground suivants afin de résoudre votre problème.


#1. Utilisation de la ClosedRange reversed()méthode

ClosedRangea une méthode appelée reversed(). reversed()method a la déclaration suivante:

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

Renvoie une vue présentant les éléments de la collection dans l'ordre inverse.

Usage:

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

for index in reversedCollection {
    print(index)
}

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

Comme alternative, vous pouvez utiliser la Range reversed()méthode:

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

for index in reversedCollection {
    print(index)
}

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

# 2. Utilisation de la sequence(first:next:)fonction

La bibliothèque standard Swift fournit une fonction appelée sequence(first:next:). sequence(first:next:)a la déclaration suivante:

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

Renvoie une séquence formée à partir d' firstapplications paresseuses répétées de next.

Usage:

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. Utilisation de la stride(from:through:by:)fonction

La bibliothèque standard Swift fournit une fonction appelée stride(from:through:by:). stride(from:through:by:)a la déclaration suivante:

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

Renvoie une séquence à partir d'une valeur de départ vers, et éventuellement incluant, une valeur de fin, en progressant du montant spécifié.

Usage:

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

for index in sequence {
    print(index)
}

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

Comme alternative, vous pouvez utiliser 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. Utilisation de l' AnyIterator init(_:)initialiseur

AnyIteratora un initialiseur appelé init(_:). init(_:)a la déclaration suivante:

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

Crée un itérateur qui encapsule la fermeture donnée dans sa next()méthode.

Usage:

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

Si nécessaire, vous pouvez refactoriser le code précédent en créant une méthode d'extension pour Intet en y enveloppant votre itérateur:

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
la source
sequence (first: 5, next: {($ 0> 0)? ($ 0 - 1): nil}) // est plus simple que ($ 0 - 1> = 0)
SirEnder
27

Pour Swift 2.0 et supérieur, vous devez appliquer l'inverse sur une collection de plage

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

Il a été renommé en .reversed () dans Swift 3.0

Andrew Luca
la source
9

Dans Swift 4 et les derniers

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

Swift 4.0

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

Si vous souhaitez inclure la tovaleur:

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

Si l'on veut parcourir un tableau ( Arrayou plus généralement n'importe lequel SequenceType) en sens inverse. Vous avez quelques options supplémentaires.

Vous pouvez d'abord reverse()le tableau et le parcourir en boucle comme d'habitude. Cependant, je préfère utiliser la enumerate()plupart du temps car il génère un tuple contenant l'objet et son index.

La seule chose à noter ici est qu'il est important de les appeler dans le bon ordre:

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

donne des index dans l'ordre décroissant (ce que j'attends généralement). tandis que:

for (index, element) in array.reverse().enumerate()(qui est une correspondance plus proche de NSArray reverseEnumerator)

parcourt le tableau en arrière mais génère des index ascendants.

Ben Lachman
la source
3

comme pour Swift 2.2, Xcode 7.3 (10, juin 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)")
}

Production :

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
la source
2

Vous pouvez envisager d'utiliser la whileboucle C-Style à la place. Cela fonctionne très bien dans Swift 3:

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

Vous pouvez utiliser la méthode reverse () pour inverser facilement les valeurs.

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

La méthode reverse () inverse les valeurs.

PU SIDHARTH
la source
Pourquoi ajouteriez-vous une réponse identique à celle déjà existante? (Par exemple, @ 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
la source