Minore di o maggiore di nell'istruzione switch Swift


145

Ho familiarità con le switchdichiarazioni in Swift, ma mi chiedo come sostituire questo pezzo di codice con un switch:

if someVar < 0 {
    // do something
} else if someVar == 0 {
    // do something else
} else if someVar > 0 {
    // etc
}

Sebbene questa sia una domanda interessante, penso che il codice che utilizza switch sia molto meno leggibile delle istruzioni if. Solo perché puoi, non significa che dovresti.
Rog,

Risposte:


241

Ecco un approccio. Supponendo che someVarsia uno Into l'altro Comparable, è possibile assegnare facoltativamente l'operando a una nuova variabile. Questo ti permette di mirare come vuoi usando la whereparola chiave:

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

Questo può essere semplificato un po ':

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

Puoi anche evitare la whereparola chiave interamente con la corrispondenza dell'intervallo:

switch someVar {
case Int.min..<0:
    print("someVar is \(someVar)")
case 0:
    print("someVar is 0")
default:
    print("someVar is \(someVar)")
}

9
Raccomando default: fatalError()di rilevare presto possibili errori logici.
Martin R,

1
Grazie! Questi esempi sono molto utili e risolvono il mio problema! (anche altri esempi erano buoni, ma i tuoi mi sono stati di grande aiuto)
Pieter,

1
@MartinR assertionFailuresembra essere un'opzione più sicura, specialmente quando si lavora in gruppo.
Michael Voline,

119

Con Swift 5, puoi scegliere una delle seguenti opzioni per sostituire la tua dichiarazione if.


# 1 Utilizzo di switch con PartialRangeFromePartialRangeUpTo

let value = 1

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

# 2 Uso dell'interruttore con ClosedRangeeRange

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 Utilizzo di switch con clausola 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 Utilizzo di switch con clausola where e assegnazione a _

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 Utilizzo di switch con RangeExpressionl' ~=(_:_:)operatore del protocollo

let value = 1

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

# 6 Utilizzo di switch con Equatablel' ~=(_:_:)operatore del protocollo

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 Usando switch con PartialRangeFrom, PartialRangeUpToe RangeExpressionil contains(_:)metodo

let value = 1

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

1
perché il caso di default è necessario nel n. 2? sembra flakey che se il rannge è da Int.min a Int.max cosa rimane?
μολὼν.λαβέ

Wow, bella lista di opzioni. Buono a sapersi, ci sono molti modi per farlo.
Christopher Pickslay,

2
Buona panoramica ma imperfetto perché i numeri tra 0 e 1 non sono considerati. 0.1genera un errore fatale perché 1...copre solo numeri da 1. Quindi questa soluzione funziona solo se valueè un Intma che è pericoloso perché se il tipo di variabile cambia la funzionalità si interrompe senza alcun errore del compilatore.
Manuel,

1
La soluzione non funziona correttamente per il tipo doppio. caso 1 ...: stampa ("maggiore di zero") NON è maggiore di 0 è maggiore o uguale a 1.
Vlad

20

La switchdichiarazione, sotto il cofano, utilizza l' ~=operatore. Così questo:

let x = 2

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

Desugars a questo:

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

Se guardi il riferimento alla libreria standard, può dirti esattamente quale ~= deve fare il sovraccarico : incluso è il range-matching e equivale a cose equabili. (Non è inclusa la corrispondenza enum case, che è una caratteristica del linguaggio, piuttosto che una funzione nella libreria std)

Vedrai che non corrisponde a un booleano diretto sul lato sinistro. Per questo tipo di confronti, è necessario aggiungere un'istruzione where.

A meno che ... non sovraccarichi l' ~=operatore da solo. (Questo è generalmente non raccomandata) Una possibilità potrebbe essere qualcosa di simile:

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

Quindi corrisponde a una funzione che restituisce un valore booleano a sinistra al suo parametro a destra. Ecco il tipo di cosa per cui potresti usarlo:

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

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

Nel tuo caso, potresti avere un'affermazione simile a questa:

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

Ma ora devi definire nuovo isNegativeeisPositive funzioni. A meno che non sovraccarichi altri operatori ...

È possibile sovraccaricare i normali operatori infix per essere operatori currix o postfix. Ecco un esempio:

postfix operator < {}

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

Funzionerebbe così:

let isGreaterThanFive = 5<

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

Combinalo con la funzione precedente e la tua istruzione switch può apparire così:

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

Ora, probabilmente non dovresti usare questo genere di cose in pratica: è un po 'complicato. Probabilmente stai meglio con la wheredichiarazione. Detto questo, il modello di istruzione switch di

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

o

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

Sembra abbastanza comune per cui valga la pena considerare.


1
dov'è la tua risposta alla domanda? Non riesco a trovarlo
Miele,

1
caso 3 .. <5: stampa (3 .. <5) - Letteralmente nel primo paragrafo. Questa risposta è sottovalutata. Mi fa risparmiare così tanto codice.
Karim,

14

Puoi:

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

6

Dal momento che qualcuno ha già pubblicato case let x where x < 0:qui è un'alternativa per dove si someVartrova un Int.

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

Ed ecco un'alternativa per dove someVarè un Double:

case -(Double.infinity)...0: // do something
// etc

6

Ecco come si presenta con le gamme

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"
}

3

L' <0espressione non funziona (più?) Quindi ho finito con questo:

Swift 3.0:

switch someVar {
    case 0:
        // it's zero
    case 0 ..< .greatestFiniteMagnitude:
        // it's greater than zero
    default:
        // it's less than zero
    }

1
In swift 3.0, X_MAXè stato sostituito da .greatestFiniteMagnitude, ad esempio Double.greatestFiniteMagnitude, CGFloat.greatestFiniteMagnitudeecc. Quindi di solito, puoi farlo solo case 0..< .greatestFiniteMagnitudeperché il tipo di someVarè già noto
Guig

@Dorian Roy var timeLeft = 100 switch timeLeft {case 0...<=7200: print("ok") default:print("nothing") }Perché l' <=operatore non viene riconosciuto? Se lo scrivo senza eguali funziona. Grazie
bibscy il

@bibscy Si desidera utilizzare l'operatore a intervallo chiuso: case 0...7200:l'operatore <=è un operatore di confronto. In uno switch puoi usare solo operatori di range (vedi documenti)
Dorian Roy

Questo è stato grandioso. Stavo ottenendo questo modello di espressione di errore di tipo 'Range <Double>' non può corrispondere ai valori di tipo 'Int' perché il mio someVarera un Inte ho dovuto fare Double(qualche Var) `per farlo funzionare ...
Miele

2

Sono contento che Swift 4 risolva il problema:

Come soluzione alternativa in 3 ho fatto:

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

Funziona ma non è l'ideale

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.