Inférieur ou supérieur à dans l'instruction Swift Switch

145

Je connais les switchinstructions de Swift, mais je me demande comment remplacer ce morceau de code par un switch:

if someVar < 0 {
    // do something
} else if someVar == 0 {
    // do something else
} else if someVar > 0 {
    // etc
}
Pieter
la source
Bien que ce soit une question intéressante, je pense que le code utilisant switch est beaucoup moins lisible que les instructions if. Ce n'est pas parce que vous pouvez, que vous devriez le faire.
Rog

Réponses:

241

Voici une approche. En supposant que someVarc'est un Intou un autre Comparable, vous pouvez éventuellement affecter l'opérande à une nouvelle variable. Cela vous permet de l'étendre comme vous le souhaitez en utilisant le wheremot - clé:

var someVar = 3

switch someVar {
case let x where x < 0:
    print("x is \(x)")
case let x where x == 0:
    print("x is \(x)")
case let x where x > 0:
    print("x is \(x)")
default:
    print("this is impossible")
}

Cela peut être un peu simplifié:

switch someVar {
case _ where someVar < 0:
    print("someVar is \(someVar)")
case 0:
    print("someVar is 0")
case _ where someVar > 0:
    print("someVar is \(someVar)")
default:
    print("this is impossible")
}

Vous pouvez également éviter wherecomplètement le mot - clé avec la correspondance de plage:

switch someVar {
case Int.min..<0:
    print("someVar is \(someVar)")
case 0:
    print("someVar is 0")
default:
    print("someVar is \(someVar)")
}
Aaron Brager
la source
9
Je recommande default: fatalError()de détecter tôt les éventuelles erreurs de logique.
Martin R
1
Merci! Ces exemples sont très utiles et résolvent mon problème! (d'autres exemples étaient bons aussi, mais les vôtres m'ont été très utiles)
Pieter
1
@MartinR assertionFailuresemble être une option plus sûre, en particulier lorsque vous travaillez en équipe.
Michael Voline
119

Avec Swift 5, vous pouvez choisir l'un des commutateurs suivants afin de remplacer votre instruction if.


# 1 Utilisation du commutateur avec PartialRangeFrometPartialRangeUpTo

let value = 1

switch value {
case 1...:
    print("greater than zero")
case 0:
    print("zero")
case ..<0:
    print("less than zero")
default:
    fatalError()
}

# 2 Utilisation du commutateur avec ClosedRangeetRange

let value = 1

switch value {
case 1 ... Int.max:
    print("greater than zero")
case Int.min ..< 0:
    print("less than zero")
case 0:
    print("zero")
default:
    fatalError()
}

# 3 Utilisation de switch avec la clause where

let value = 1

switch value {
case let val where val > 0:
    print("\(val) is greater than zero")
case let val where val == 0:
    print("\(val) is zero")
case let val where val < 0:
    print("\(val) is less than zero")
default:
    fatalError()
}

# 4 Utilisation de commutateur avec clause where et affectation à _

let value = 1

switch value {
case _ where value > 0:
    print("greater than zero")
case _ where value == 0:
    print("zero")
case _ where value < 0:
    print("less than zero")
default:
    fatalError()
}

# 5 Utilisation du commutateur avec RangeExpressionl' ~=(_:_:)opérateur du protocole

let value = 1

switch true {
case 1... ~= value:
    print("greater than zero")
case ..<0 ~= value:
    print("less than zero")
default:
    print("zero")
}

# 6 Utilisation du commutateur avec Equatablel' ~=(_:_:)opérateur du protocole

let value = 1

switch true {
case value > 0:
    print("greater than zero")
case value < 0:
    print("less than zero")
case 0 ~= value:
    print("zero")
default:
    fatalError()
}

# 7 Utilisation de commutateur avec PartialRangeFrom, PartialRangeUpToet RangeExpressionde » contains(_:)méthode

let value = 1

switch true {
case (1...).contains(value):
    print("greater than zero")
case (..<0).contains(value):
    print("less than zero")
default:
    print("zero")
}
Imanou Petit
la source
1
pourquoi le cas par défaut est-il nécessaire dans # 2? semble floconneux que si le rannge est de Int.min à Int.max que reste-t-il?
μολὼν.λαβέ
Wow, belle liste d'options. Il est bon de savoir qu'il existe plusieurs façons de procéder.
Christopher Pickslay
2
Bon aperçu mais imparfait car les nombres entre 0 et 1 ne sont pas comptabilisés. 0.1renvoie une erreur fatale car 1...ne couvre que les nombres à partir de 1. Cette solution ne fonctionne donc que si valuec'est un Intmais c'est dangereux car si le type de variable change, la fonctionnalité est interrompue sans aucune erreur du compilateur.
Manuel
1
Votre solution ne fonctionne pas correctement pour le type Double. cas 1 ...: print ("supérieur à zéro") N'est PAS supérieur à 0, il est supérieur ou égal à 1.
Vlad
20

La switchdéclaration, sous le capot, utilise l' ~=opérateur. Donc ça:

let x = 2

switch x {
case 1: print(1)
case 2: print(2)
case 3..<5: print(3..<5)
default: break
}

Desugars à ceci:

if 1          ~= x { print(1) }
else if 2     ~= x { print(2) }
else if 3..<5 ~= x { print(3..<5) }
else {  }

Si vous regardez la référence de bibliothèque standard, elle peut vous dire exactement ce que le ~=est surchargé à faire : inclus est la correspondance de plage, et l'équation pour des choses égales. (La correspondance enum-case n'est pas incluse, qui est une fonctionnalité de langage, plutôt qu'une fonction dans la bibliothèque std)

Vous verrez qu'il ne correspond pas à un booléen droit sur le côté gauche. Pour ce genre de comparaisons, vous devez ajouter une instruction where.

Sauf si ... vous surchargez l' ~=opérateur vous-même. (Ce n'est généralement pas recommandé) Une possibilité serait quelque chose comme ceci:

func ~= <T> (lhs: T -> Bool, rhs: T) -> Bool {
  return lhs(rhs)
}

Cela correspond donc à une fonction qui renvoie un booléen à gauche à son paramètre à droite. Voici le genre de chose pour laquelle vous pouvez l'utiliser:

func isEven(n: Int) -> Bool { return n % 2 == 0 }

switch 2 {
case isEven: print("Even!")
default:     print("Odd!")
}

Pour votre cas, vous pourriez avoir une déclaration qui ressemble à ceci:

switch someVar {
case isNegative: ...
case 0: ...
case isPositive: ...
}

Mais maintenant, vous devez définir de nouveaux isNegativeetisPositive fonctions. Sauf si vous surchargez plus d'opérateurs ...

Vous pouvez surcharger les opérateurs d'infixe normaux pour qu'ils soient des opérateurs de préfixe curry ou de suffixe. Voici un exemple:

postfix operator < {}

postfix func < <T : Comparable>(lhs: T)(_ rhs: T) -> Bool {
  return lhs < rhs
}

Cela fonctionnerait comme ceci:

let isGreaterThanFive = 5<

isGreaterThanFive(6) // true
isGreaterThanFive(5) // false

Combinez cela avec la fonction précédente, et votre instruction switch peut ressembler à ceci:

switch someVar {
case 0< : print("Bigger than 0")
case 0  : print("0")
default : print("Less than 0")
}

Maintenant, vous ne devriez probablement pas utiliser ce genre de chose dans la pratique: c'est un peu douteux. Vous feriez (probablement) mieux de vous en tenir à la wheredéclaration. Cela dit, le modèle d'instruction switch de

switch x {
case negative:
case 0:
case positive:
}

ou

switch x {
case lessThan(someNumber):
case someNumber:
case greaterThan(someNumber):
}

Cela semble assez courant pour que cela vaille la peine d'être considéré.

oisdk
la source
1
où est votre réponse à la question? Je ne peux pas le trouver.
Honey
1
cas 3 .. <5: print (3 .. <5) - Littéralement dans le premier paragraphe. Cette réponse est sous-estimée. M'économise tellement de code.
Karim
14

Vous pouvez:

switch true {
case someVar < 0:
    print("less than zero")
case someVar == 0:
    print("eq 0")
default:
    print("otherwise")
}
Rintaro
la source
6

Puisque quelqu'un a déjà posté case let x where x < 0:ici, c'est une alternative pour où someVarest un fichier Int.

switch someVar{
case Int.min...0: // do something
case 0: // do something
default: // do something
}

Et voici une alternative pour où someVar est un Double:

case -(Double.infinity)...0: // do something
// etc
Simons
la source
6

Voici à quoi ça ressemble avec les gammes

switch average {
case 0..<40: //greater or equal than 0 and less than 40
    return "T"
case 40..<55: //greater or equal than 40 and less than 55
    return "D"
case 55..<70: //greater or equal than 55 and less than 70
    return "P"
case 70..<80: //greater or equal than 70 and less than 80
    return "A"
case 80..<90: //greater or equal than 80 and less than 90
    return "E"
case 90...100: //greater or equal than 90 and less or equal than 100
    return "O"
default:
    return "Z"
}
GOrozco58
la source
3

L' <0expression ne fonctionne pas (plus?) Alors j'ai fini avec ceci:

Swift 3.0:

switch someVar {
    case 0:
        // it's zero
    case 0 ..< .greatestFiniteMagnitude:
        // it's greater than zero
    default:
        // it's less than zero
    }
Dorian Roy
la source
1
Dans swift 3.0, X_MAXa été remplacé par .greatestFiniteMagnitude, c'est-à-dire Double.greatestFiniteMagnitude, CGFloat.greatestFiniteMagnitudeetc. Donc, généralement, vous pouvez le faire case 0..< .greatestFiniteMagnitudepuisque le type de someVarest déjà connu
Guig
@Dorian Roy var timeLeft = 100 switch timeLeft {case 0...<=7200: print("ok") default:print("nothing") }Pourquoi l' <=opérateur n'est-il pas reconnu? Si je l'écris sans égal, cela fonctionne. Merci
bibscy
@bibscy Vous souhaitez utiliser l'opérateur de plage fermée: case 0...7200:L'opérateur <=est un opérateur de comparaison. Dans un commutateur, vous ne pouvez utiliser que des opérateurs de plage (voir la documentation)
Dorian Roy
C'était génial. J'obtenais ce modèle d'expression d' erreur de type 'Range <Double>' ne peut pas correspondre aux valeurs de type 'Int' parce que j'étais someVarun Intet je devais faire Double(someVar) `pour le faire fonctionner ...
Chérie
2

Heureux que Swift 4 résout le problème:

Comme solution de contournement dans 3, j'ai fait:

switch translation.x  {
case  0..<200:
    print(translation.x, slideLimit)
case  -200..<0:
    print(translation.x, slideLimit)
default:
    break
}

Fonctionne mais pas idéal

Jeremy Andrews
la source