Plage inverse dans Swift

Existe-t-il un moyen de travailler avec des plages inverses dans Swift?

Par exemple:

for i in 5...1 {
  // do something
}

Est une boucle infinie.

Je sais que je peux utiliser 1..5 à la place, calculer j = 6 - i et utiliser j comme index. Je me demandais s'il y avait quelque chose de plus lisible?

80
demandé sur Eduardo 2014-06-23 22:10:31

7 réponses

Mise à jour pour la dernière Swift 3 (fonctionne toujours dans Swift 4)

Vous pouvez utiliser la méthode reversed() sur une plage

for i in (1...5).reversed() { print(i) } // 5 4 3 2 1

Ou stride(from:through:by:) méthode

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

stide(from:to:by:) est similaire, mais exclut la dernière valeur

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

Mise à jour pour la dernière Swift 2

Tout d'abord, les extensions de protocole changent la façon dont reverse est utilisé:

for i in (1...5).reverse() { print(i) } // 5 4 3 2 1

Stride a été retravaillé dans Xcode 7 Beta 6. La nouvelle utilisation est:

for i in 0.stride(to: -8, by: -2) { print(i) } // 0 -2 -4 -6
for i in 0.stride(through: -8, by: -2) { print(i) } // 0 -2 -4 -6 -8

Cela fonctionne aussi pour Doubles:

for i in 0.5.stride(to:-0.1, by: -0.1) { print(i) }

Méfiez-vous des comparaisons en virgule flottante ici pour les limites.

Plus Tôt modifier pour Swift 1.2: de Xcode 6 Bêta 4, par et ReverseRange n'existent plus :[

Si vous cherchez simplement à inverser une plage, la fonction reverse est tout ce dont vous avez besoin:

for i in reverse(1...5) { println(i) } // prints 5,4,3,2,1

Comme posté par 0x7fffffffIl y a une nouvelle construction stride qui peut être utilisée pour itérer et incrémenter par des entiers arbitraires. Apple a également déclaré que le support en virgule flottante est à venir.

Provenant de sa réponse:

for x in stride(from: 0, through: -8, by: -2) {
    println(x) // 0, -2, -4, -6, -8
}

for x in stride(from: 6, to: -2, by: -4) {
    println(x) // 6, 2
}
141
répondu Jack 2018-02-12 15:43:01

Il y a quelque chose de troublant dans l'asymétrie de ceci:

for i in (1..<5).reverse()

... par opposition à ceci:

for i in 1..<5 {

Cela signifie que chaque fois que je veux faire une plage inverse, je dois me rappeler de mettre les parenthèses, plus je dois écrire que .reverse() à la fin, qui sort comme un pouce endolori. C'est vraiment moche par rapport au style C pour les boucles, qui sont symétriques en comptant et en comptant vers le bas. J'ai donc tendance à utiliser le style C pour les boucles à la place. Mais dans Swift 2.2, le style C pour les boucles sont s'en aller! J'ai donc dû me précipiter pour remplacer toutes mes boucles de style C décrémentant avec cette construction laide .reverse() - me demandant tout le temps, pourquoi diable n'y a-t-il pas un opérateur de plage inverse?

Mais attendez! C'est Swift-nous sommes autorisés à définir nos propres opérateurs!! Nous y voilà:

infix operator >>> {
    associativity none
    precedence 135
}

func >>> <Pos : ForwardIndexType where Pos : Comparable>(end:Pos, start:Pos)
    -> ReverseRandomAccessCollection<(Range<Pos>)> {
        return (start..<end).reverse()
}

Alors maintenant, je suis autorisé à dire:

for i in 5>>>1 {print(i)} // 4, 3, 2, 1

Cela couvre juste le cas le plus commun qui se produit dans mon code, mais c'est de loin le cas le plus commun, donc c'est tout ce dont j'ai besoin actuellement.

, j'ai eu une sorte de crise interne à venir avec l'opérateur. J'aurais bien aimé utiliser >.., comme étant l'inverse de ..<, mais ce n'est pas juridique: vous ne pouvez pas utiliser un point après un non-point, il semble. J'ai considéré ..> mais j'ai décidé qu'il était trop difficile de distinguer de ..<. La bonne chose à propos de >>> est qu'il vous crie: "down to!"(Bien sûr, vous êtes libre de trouver un autre opérateur. Mais mon conseil est: pour la super symétrie, définissez <<< pour faire quoi ..<, et maintenant vous avez <<< et >>>, qui sont symétriques et facile à saisir.)


Swift 3 version (Xcode 8 graine 6):

infix operator >>> : RangeFormationPrecedence
func >>><Bound>(maximum: Bound, minimum: Bound) ->
    ReversedRandomAccessCollection<CountableRange<Bound>> 
    where Bound : Comparable, Bound.Stride : Integer {
        return (minimum..<maximum).reversed()
}

Version Swift 4 (Xcode 9 beta 3):

infix operator >>> : RangeFormationPrecedence
func >>><Bound>(maximum: Bound, minimum: Bound)
    -> ReversedRandomAccessCollection<CountableRange<Bound>>
    where Bound : Comparable & Strideable { 
        return (minimum..<maximum).reversed()
}

Version Swift 4.2 (Xcode 10 beta 1):

infix operator >>> : RangeFormationPrecedence
func >>><Bound>(maximum: Bound, minimum: Bound)
    -> ReversedRandomAccessCollection<Range<Bound>>
    where Bound : Strideable { 
        return (minimum..<maximum).reversed()
}
15
répondu matt 2018-06-05 15:24:14

Il semble que les réponses à cette question ont un peu changé au fur et à mesure que nous avons progressé à travers les bêtas. Depuis la version bêta 4, la fonction by() et le type ReversedRange ont été supprimés de la langue. Si vous cherchez à faire une plage inversée, vos options sont maintenant les suivantes:

1: créez une plage avant, puis utilisez la fonction reverse() pour l'Inverser.

for x in reverse(0 ... 4) {
    println(x) // 4, 3, 2, 1, 0
}

for x in reverse(0 ..< 4) {
    println(x) // 3, 2, 1, 0
}

2: Utilisez les nouvelles fonctions stride() ajoutées dans beta 4, qui incluent des fonctions pour spécifier le démarrage et index de fin, ainsi que le montant à parcourir.

for x in stride(from: 0, through: -8, by: -2) {
    println(x) // 0, -2, -4, -6, -8
}

for x in stride(from: 6, to: -2, by: -4) {
    println(x) // 6, 2
}

Notez que j'ai également inclus le nouvel opérateur de gamme exclusif dans ce post. .. a été remplacé par ..<.

Edit: à partir des notes de version de Xcode 6 beta 5 , Apple a ajouté la suggestion suivante pour gérer ceci:

ReverseRange a été supprimé; utilisez lazy (X..

Voici un exemple.

for i in lazy(0...5).reverse() {
    // 0, 1, 2, 3, 4, 5
}
9
répondu Mick MacCallum 2014-08-04 21:12:32

Xcode 7, Bêta 2:

for i in (1...5).reverse() {
  // do something
}
5
répondu leanne 2015-07-05 21:50:06

Swift 3, 4+: vous pouvez le faire comme ceci:

for i in sequence(first: 10, next: {$0 - 1}) {

    guard i >= 0 else {
        break
    }
    print(i)
}

Résultat: 10, 9, 8 ... 0

Vous pouvez le personnaliser comme vous le souhaitez. Pour plus d'informations, lisez le func sequence<T> référence

3
répondu nCod3d 2018-04-03 15:08:46

Cela pourrait être une autre façon de le faire.

(1...5).reversed().forEach { print($0) }
0
répondu David H 2017-12-20 13:39:45

La fonction

Reverse() est utilisée pour le nombre inverse.

Var n: Int / / entrez le numéro

Pour i en 1...Num.inverser() { Print(je) }

-1
répondu laxrajpurohit 2018-08-13 11:36:13