Menor o mayor que en la declaración de cambio rápido

145

Estoy familiarizado con las switchdeclaraciones en Swift, pero me pregunto cómo reemplazar este fragmento de código con un switch:

if someVar < 0 {
    // do something
} else if someVar == 0 {
    // do something else
} else if someVar > 0 {
    // etc
}
Pieter
fuente
Aunque esta es una pregunta interesante, creo que el código que usa switch es mucho menos legible que las declaraciones if. Solo porque puedas, no significa que debas.
Rog

Respuestas:

241

Aquí hay un enfoque. Suponiendo que someVares uno Intu otro Comparable, opcionalmente puede asignar el operando a una nueva variable. Esto le permite definir el alcance como desee con la wherepalabra clave:

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

Esto se puede simplificar un poco:

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

También puede evitar la wherepalabra clave por completo con la coincidencia de rango:

switch someVar {
case Int.min..<0:
    print("someVar is \(someVar)")
case 0:
    print("someVar is 0")
default:
    print("someVar is \(someVar)")
}
Aaron Brager
fuente
9
Recomiendo default: fatalError()detectar posibles errores lógicos temprano.
Martin R
1
¡Gracias! ¡Estos ejemplos son muy útiles y resuelven mi problema! (otros ejemplos también fueron buenos, pero los suyos fueron de gran ayuda para mí)
Pieter
1
@MartinR assertionFailureparece ser una opción más segura, especialmente cuando se trabaja en equipo.
Michael Voline
119

Con Swift 5, puede elegir uno de los siguientes interruptores para reemplazar su declaración if.


# 1 Usando el interruptor con PartialRangeFromyPartialRangeUpTo

let value = 1

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

# 2 Usando el interruptor con ClosedRangeyRange

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 Usando el interruptor con la cláusula 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 Uso de switch con cláusula where y asignación 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 Usando el interruptor con RangeExpressionel ~=(_:_:)operador del protocolo

let value = 1

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

# 6 Usando el interruptor con Equatableel ~=(_:_:)operador del protocolo

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 el interruptor con PartialRangeFrom, PartialRangeUpToy RangeExpressionel contains(_:)método de

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
fuente
1
¿Por qué se necesita el caso predeterminado en el n. ° 2? Parece extraño que si el rango es de Int.min a Int.max, ¿qué queda?
μολὼν.λαβέ
Wow, buena lista de opciones. Es bueno saber que hay varias maneras de hacer esto.
Christopher Pickslay
2
Buena visión general pero defectuosa porque los números entre 0 y 1 no se tienen en cuenta. 0.1arroja un error fatal porque 1...cubre solo números del 1. Por lo tanto, esta solución solo funciona si valuees un Intpero eso es peligroso porque si el tipo de variable cambia la funcionalidad se rompe sin ningún error del compilador.
Manuel
1
Su solución no funciona correctamente para el tipo Doble. caso 1 ...: print ("mayor que cero") NO es mayor que 0 es mayor o igual a 1.
Vlad
20

La switchdeclaración, debajo del capó, utiliza el ~=operador. Así que esto:

let x = 2

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

Desugar a esto:

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

Si observa la referencia de la biblioteca estándar, puede decirle exactamente para qué ~=está sobrecargada : se incluye la coincidencia de rango y la ecuación para cosas equiparables. (No se incluye la coincidencia de mayúsculas y minúsculas, que es una característica del lenguaje, en lugar de una función en la biblioteca estándar)

Verá que no coincide con un booleano recto en el lado izquierdo. Para ese tipo de comparaciones, debe agregar una declaración where.

A menos que ... sobrecargues el ~=operador tú mismo. (Esto generalmente no se recomienda) Una posibilidad sería algo como esto:

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

Entonces, coincide con una función que devuelve un valor booleano a la izquierda a su parámetro a la derecha. Este es el tipo de cosas para las que podría usarlo:

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

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

Para su caso, es posible que tenga una declaración que se vea así:

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

Pero ahora tienes que definir nuevas isNegativey isPositivefunciones. A menos que sobrecargue algunos operadores más ...

Puede sobrecargar los operadores de infijo normales para ser operadores de prefijo o postfix curry. Aquí hay un ejemplo:

postfix operator < {}

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

Esto funcionaría así:

let isGreaterThanFive = 5<

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

Combine eso con la función anterior, y su declaración de cambio puede verse así:

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

Ahora, probablemente no deberías usar este tipo de cosas en la práctica: es un poco dudoso. Es (probablemente) mejor seguir con la wheredeclaración. Dicho esto, el patrón de declaración de cambio de

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

o

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

Parece lo suficientemente común como para que valga la pena considerarlo.

oisdk
fuente
1
¿Dónde está tu respuesta a la pregunta? No puedo encontrarlo
Miel el
1
caso 3 .. <5: print (3 .. <5) - Literalmente en el primer párrafo. Esta respuesta está subestimada. Me ahorra mucho código.
Karim
14

Usted puede:

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

Dado que alguien ya ha publicado case let x where x < 0:aquí, es una alternativa para dónde someVares un Int.

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

Y aquí hay una alternativa de dónde someVares un Double:

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

Así es como se ve con los rangos

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
fuente
3

La <0expresión no funciona (¿ya?), Así que terminé con esto:

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
fuente
1
En swift 3.0, X_MAXha sido reemplazado por .greatestFiniteMagnitude, es decir Double.greatestFiniteMagnitude, CGFloat.greatestFiniteMagnitudeetc. Por lo general, puede hacerlo case 0..< .greatestFiniteMagnitudeya que el tipo de someVarya se conoce
Guig
@Dorian Roy var timeLeft = 100 switch timeLeft {case 0...<=7200: print("ok") default:print("nothing") }¿Por qué <=no se reconoce al operador? Si lo escribo sin igual funciona. Gracias
bibscy
@bibscy Desea utilizar el operador de rango cerrado: case 0...7200:el operador <=es un operador de comparación. En un switch solo puedes usar operadores de rango (ver documentos)
Dorian Roy
Esto fue genial. Estaba obteniendo este patrón de expresión de error del tipo 'Rango <Doble>' no puede coincidir con los valores del tipo 'Int' porque mi someVarera Inty tenía que hacer Double(algoVar) `para que funcione ...
Miel
2

Me alegra que Swift 4 aborde el problema:

Como solución en 3 hice:

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

Funciona pero no es ideal

Jeremy Andrews
fuente