¿Puedo usar el operador de rango con la declaración if en Swift?

196

¿Es posible usar el operador de rango ...y ..<con la instrucción if? Maye algo como esto:

let statusCode = 204
if statusCode in 200 ..< 299 {
  NSLog("Success")
}
Palanqueta
fuente

Respuestas:

425

Puede usar el operador "coincidencia de patrones" ~=:

if 200 ... 299 ~= statusCode {
    print("success")
}

O una declaración de cambio con un patrón de expresión (que utiliza el operador de coincidencia de patrones internamente):

switch statusCode {
case 200 ... 299:
    print("success")
default:
    print("failure")
}

Tenga en cuenta que ..<denota un rango que omite el valor superior, por lo que probablemente desee 200 ... 299o 200 ..< 300.

Información adicional: cuando el código anterior se compila en Xcode 6.3 con las optimizaciones activadas, entonces para la prueba

if 200 ... 299 ~= statusCode

en realidad no se genera ninguna llamada de función, solo tres instrucciones de ensamblaje:

addq    $-200, %rdi
cmpq    $99, %rdi
ja  LBB0_1

este es exactamente el mismo código de ensamblado que se genera para

if statusCode >= 200 && statusCode <= 299

Puedes verificar eso con

xcrun -sdk macosx swiftc -O -emit-assembly main.swift

A partir de Swift 2, esto se puede escribir como

if case 200 ... 299 = statusCode {
    print("success")
}

utilizando la coincidencia de patrones recientemente introducida para sentencias if. Consulte también Swift 2: coincidencia de patrones en "if" .

Martin R
fuente
1
Genial, ¿es esto O (1)? Además, sería bueno si Swift tuviera una mano corta para cambiar las declaraciones, como Scala, por ejemplo. Pero dado que siempre estás obligado a manejar todas las posibilidades en tiempo de compilación en Swift, puede que no sea realmente factible.
Sky
2
@ Sky: Del código de ensamblaje generado se puede ver que func ~= (Range<A>, A) -> Boolse llama a una función de biblioteca . Me gustaría suponer que esta función trabaja con O (1).
Martin R
44
@Downvoter: Algún comentario explicativo sería bueno, para que pueda mejorar o corregir la respuesta ...
Martin R
1
@MartinR, ¿cómo saber qué función llama el lenguaje ensamblador? +1 para una respuesta genial
codificador
3
@codester: compilé el código en la línea de comando xcrun -sdk macosx swift -emit-assembly main.swifte inspeccioné el código de ensamblaje. Luego solía xcrun swift-demangle ...desorganizar el nombre de la función llamada. - Desafortunadamente, Xcode aún no puede crear código de ensamblaje para archivos Swift, quizás funcione en una versión posterior.
Martin R
95

Esta versión parece ser más legible que la coincidencia de patrones:

if (200 ... 299).contains(statusCode) {
    print("Success")
}
Serhii Yakovenko
fuente
2
Exactamente lo que estaba buscando
Nazim Kerimbekov
Me sale este error => No se puede formar Rango con upperBound <lowerBound
Alfi
9

Este es un hilo viejo, pero me parece que estamos pensando demasiado en esto. Me parece que la mejor respuesta es solo

if statusCode >= 200 && statusCode <= 299

No hay

if 200 > statusCode > 299

que yo conozco, y las otras soluciones sugeridas están haciendo llamadas a funciones, que son más difíciles de leer y pueden ser más lentas de ejecutar. El método de coincidencia de patrones es un truco útil para conocer, pero parece ser un mal ajuste para este problema.

Editar:

Personalmente, considero que el operador de coincidencia de patrones es horrible, y deseo que el compilador admita la if x in 1...100sintaxis. Eso es muuuucho más intuitivo y fácil de leer queif 1...100 ~= x

Duncan C
fuente
1
Tienes razón en que esta versión es mejor para leer, solo traté de responder la pregunta explícita "¿Es posible usar el operador de rango ...?" - Pero Xcode 6.3 beta (en modo optimizado) genera exactamente tres instrucciones de ensamblaje para if 200 ... 299 ~= statusCode, sin llamada de función :)
Martin R
13
Realmente if 200 ... 299 ~= statusCodeda el mismo código de ensamblaje queif statusCode >= 200 && statusCode <= 299
Martin R
66
A menos que este condicional se encuentre en una sección crítica que se visite miles de veces por segundo, preocuparse por la sobrecarga de las llamadas de función es la optimización prematura. Incluso entonces, me preocuparía más lo que está haciendo una llamada de función que el costo de llamarla. Sin embargo, es un buen trabajo @MartinR para demostrar que no hay costo.
rickster
1
@rickster, bastante cierto. Todavía tiendo a preferir construcciones eficientes sobre las ineficientes como una cuestión de costumbre (suponiendo que la legibilidad sea similar). No en la medida en que pierda demasiado de MI tiempo en él, pero aún así vale la pena saber cuáles son los costos de los diferentes enfoques.
Duncan C
1
Esto es un poco curioso, pero no estoy de acuerdo con su sugerencia de que su declaración if es más legible o comprensible que la respuesta publicada por @SerhiiYakovenko. Simplemente sobre la base de DRY: usted nombra "statusCode" dos veces. En una sesión de depuración de ojos nublados a altas horas de la noche, después de haber decidido que una variable diferente llamada "statusValue" debería usarse aquí en lugar de "statusCode", podría cometer el error de cambiar uno de los nombres de variables y no el otro .
RenniePet
3

Quería verificar los errores 4xx excepto 401. Aquí está el código:

let i = 401

if 400..<500 ~= i, i != 401 {
    print("yes")
} else {
    print("NO")
}
abhimuralidharan
fuente
2

También preferí el operador Range .contains (), hasta que descubrí que su implementación es ineficiente - https://oleb.net/blog/2015/09/swift-ranges-and-intervals/

Podemos representar la condición x <0 usando un rango: (Int.min .. <0) .contains (x) es exactamente equivalente. Sin embargo, es mucho más lento. La implementación predeterminada de contiene (_ :) atraviesa toda la colección, y ejecutar un bucle nueve quintillones de veces en el peor de los casos no es barato.

Entrada
fuente