Comment itérer pour une boucle dans l'ordre inverse dans swift?

Quand j'utilise la boucle for dans Playground, tout a bien fonctionné, jusqu'à ce que j'ai changé le premier paramètre de la boucle pour être la valeur la plus élevée. (itéré dans l'ordre décroissant)

Est-ce un bug? Est-ce que quelqu'un d'autre l'a eu?

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

Le countur qui affiche le nombre d'itérations qui seront exécutées continue à cocher …

entrez la description de l'image ici

Xcode 6 beta 4 ajoute deux fonctions pour itérer sur les plages avec une étape autre que one: ssortingde(from: to: by:) , qui est utilisé avec des plages exclusives et ssortingde(from: through: by:) , qui est utilisé avec inclusive gammes.

Pour parcourir une plage dans l'ordre inverse, ils peuvent être utilisés comme suit:

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

Notez que ni l'une ni l'autre n'est une fonction membre Range . Ce sont des fonctions globales qui returnnent une SsortingdeTo ou SsortingdeThrough , qui sont définies différemment de la structure Range .

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

Appliquez la fonction inverse à la plage pour revenir en arrière:

Pour Swift 1.2 et plus tôt:

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

Note: reverse(1...10) crée un tableau de type [Int] , donc bien que cela puisse convenir aux petites plages, il serait sage d'utiliser lazy comme indiqué ci-dessous ou de considérer la réponse de ssortingde acceptée si votre range est large .


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

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 en 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>)> , cela fonctionne donc 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 } 

Mise à 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 

Vers l'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 

Vers l'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 à travers une collection. Pour cela, vous pouvez utiliser enumerate() , qui returnne un tuple. Le premier élément du tuple est l'index et le second élément est l'object.

 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 

Vers l'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 voudrez probablement faire .enumerated().reversed() plutôt que .reversed().enumerated() (ce qui augmenterait le nombre d'index).

foulée: numbers

La foulée est un moyen d'itérer sans utiliser de plage. Il y a deux forms. Les commentaires à la fin du code montrent ce que serait la version de la gamme (en supposant que la taille de l'incrément est 1).

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

Vers l'avant

 for index in ssortingde(from: 0, to: 5, by: 1) { print(index) } // 0 // 1 // 2 // 3 // 4 

Vers l'arrière

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

 for index in ssortingde(from: 4, through: 0, by: -1) { print(index) } // 4 // 3 // 2 // 1 // 0 

Notez la différence to et à through .

ssortingde: les éléments de SequenceType

Transférer par incréments de 2

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

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

 for index in ssortingde(from: 0, to: 5, by: 2) { print("\(index), \(animals[index])") } // 0, horse // 2, camel // 4, goat 

Vers l'arrière

 for index in ssortingde(from: 4, through: 0, by: -1) { print("\(index), \(animals[index])") } // 0, horse // 1, cow // 2, camel // 3, sheep // 4, goat 

Remarques

  • @matt a une solution intéressante où il définit son propre opérateur inverse et l'appelle >>> . Il ne prend pas beaucoup de code à définir et est utilisé comme ceci:

     for index in 5>>>0 { print(index) } // 4 // 3 // 2 // 1 // 0 
  • Découvrez sur C-Style For Loops Supprimé de Swift 3

Swift 4 à partir de

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

Pour Swift 2.0 et supérieur, vous devez appliquer le reverse sur une collection de gamme

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

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

Avec Swift 3, selon vos besoins, vous pouvez choisir l'une des huit implémentations de code Playground suivantes afin de résoudre votre problème.


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

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

 func reversed() -> ReversedRandomAccessCollection<CountableClosedRange<Bound>> 

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

Usage:

 let reversedRandomAccessCollection = (0 ... 5).reversed() for index in reversedRandomAccessCollection { print(index) } /* Prints: 5 4 3 2 1 0 */ 

# 2. Utilisation de la méthode CountableRange reversed()

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

 func reversed() -> ReversedRandomAccessCollection<CountableRange<Bound>> 

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

Usage:

 let reversedRandomAccessCollection = (0 ..< 6).reversed() for index in reversedRandomAccessCollection { print(index) } /* Prints: 5 4 3 2 1 0 */ 

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

La bibliothèque standard de 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?) -> UnfoldSequence<T, (T?, Bool)> 

Renvoie une séquence formée à partir des first applications paresseuses répétées de next .

Usage:

 let unfoldSequence = sequence(first: 5, next: { $0 - 1 >= 0 ? $0 - 1 : nil }) for index in unfoldSequence { print(index) } /* Prints: 5 4 3 2 1 0 */ 

# 4. Utilisation de la fonction ssortingde(from:through:by:)

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

 func ssortingde<T>(from start: T, through end: T, by ssortingde: T.Ssortingde) -> SsortingdeThrough<T> where T : Ssortingdeable 

Retourne la séquence de valeurs (self, self + ssortingde, self + 2 * ssortingde, … last) où last est la dernière valeur de la progression inférieure ou égale à end .

Usage:

 let sequence = ssortingde(from: 5, through: 0, by: -1) for index in sequence { print(index) } /* Prints: 5 4 3 2 1 0 */ 

# 5. Utilisation de la fonction ssortingde(from:to:by:)

Swift Standard Library fournit une fonction appelée ssortingde(from:to:by:) . ssortingde(from:to:by:) a la déclaration suivante:

 func ssortingde<T>(from start: T, to end: T, by ssortingde: T.Ssortingde) -> SsortingdeTo<T> where T : Ssortingdeable 

Renvoie la séquence de valeurs (self, self + ssortingde, self + 2 * ssortingde, … last) où last est la dernière valeur de la progression qui est inférieure à end .

Usage:

 let sequence = ssortingde(from: 5, to: -1, by: -1) for index in sequence { print(index) } /* Prints: 5 4 3 2 1 0 */ 

# 6. Utilisation de l' init(_:) AnyIterator init(_:)

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

 init<I>(_ base: I) where I : IteratorProtocol, I.Element == Element 

Crée un iterator qui enveloppe un iterator de base mais dont le type dépend uniquement du type d'élément de l'iterator de base.

Usage:

 var index = 5 guard index >= 0 else { fatalError("index must be positive or equal to zero") } let iterator = AnyIterator<Int>({ defer { index = index - 1 } return index >= 0 ? index : nil }) for index in iterator { print(index) } /* Prints: 5 4 3 2 1 0 */ 

#7. Utilisation de l' init(_:) AnyIterator init(_:)

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

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

Crée un iterator qui enveloppe la fermeture donnée dans sa méthode next() .

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

# 8. Utilisation d'une méthode d'extension Int personnalisée

Vous pouvez refactoriser le code précédent en créant une méthode d'extension pour Int et en y incluant votre 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 */ 

Si l'on veut parcourir à travers un tableau ( Array ou plus généralement tout SequenceType ) à l'envers. Vous avez quelques options supplémentaires.

D'abord, vous pouvez reverse() le tableau et le parcourir comme d'habitude. Cependant je préfère utiliser enumerate() plupart du time car il sort un tuple contenant l'object 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 indices dans l'ordre décroissant (ce que j'attends généralement). tandis que:

for (index, element) in array.reverse().enumerate() (qui correspond plus à reverseEnumerator de NSArray)

marche vers l'arrière mais produit des index ascendants.

quant à 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)") } 

Sortie:

 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 

Swift 4.0

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

Si vous voulez inclure la valeur to :

 for i in ssortingde(from: 5, through: 0, by: -1) { print(i) // 5,4,3,2,1,0 } 
 var sum1 = 0 for i in 0...100{ sum1 += i } print (sum1) for i in (10...100).reverse(){ sum1 /= i } print(sum1) 

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

 var i = 5 while i > 0 { print(i) i -= 1 }