Operadores Básicos

Un operador es un símbolo o frase especial que se utiliza para verificar, cambiar o combinar valores. Por ejemplo, el operador de adición (+) suma dos números, como en let i = 1 + 2, mientras que el operador lógico Y (AND) —&&— combina dos valores booleanos, como en if codigoPuertaIngresado && aproboEscanerDeRetina.

Swift provee los operadores que puede que ya conozcas de lenguajes como C, y mejora varios aspectos con el fin de eliminar errores comunes de codificación. El operador de asignación (=) no devuelve un valor, esto, con el fin evitar que se utilice erróneamente cuando se pretende usar el operador «igual que» (==). Los operadores aritméticos (+, -, *, /, %, y demás) detectan y bloquean el desbordamiento de valores, para prevenir resultados inesperados al trabajar con números que se puedan hacer más grandes o más pequeños que el rango de valor permitido por el tipo que los almacena. Puedes habilitar el mecanismo de desbordamiento de valores en Swift, mediante el uso de los operadores de desbordamiento, como se describe en Operadores de desbordamiento.

Swift también proporciona «operadores de rango» —los cuales no existen en C—, tales como a..<b y a...b, como una forma concisa de expresar un rango de valores.

Este capítulo describe los operadores más comunes de Swift. En Operadores avanzados, se cubren los operadores avanzados de Swift, y se describe cómo definir tus propios operadores e implementar los operadores estándar para crear tus propios tipos.

Terminología

Los operadores pueden ser unarios, binarios o ternarios:

  • Los operadores unarios operan en un solo elemento (como -a). Los operadores unarios prefijo aparecen inmediatamente antes de su objetivo (como en !b), y los operadores unarios posfijo aparecen inmediatamente después de su objetivo (como en c!).
  • Los operadores binarios operan en dos elementos (como en 2 + 3) y son interfijos, ya que aparecen entre sus dos objetivos.
  • Los operadores ternarios operan en tres elementos. Al igual que C, Swift solo tiene un operador ternario: el operador condicional ternario (a ? b : c).

Los valores sobre los que actúa un operador se conocen como operandos. En la expresión 1 + 2, el símbolo + es un operador interfijo y sus dos operandos son los valores 1 y 2.

Operador de asignación

El operador de asignación (=) se usa para asignar un valor. Al nombre del operando a la izquierda se le asigna el valor del operando a la derecha. En la expresión a = b, se inicializa o actualiza el valor de a con el valor de b:

1let b = 10
2var a = 5
3
4a = b
5// a es ahora igual a 10

Si el lado derecho de la asignación es una tupla con múltiples valores, sus elementos se pueden descomponer en múltiples constantes o variables al mismo tiempo:

1let (x, y) = (1, 2)
2// x es igual a 1, y y es igual a 2

A diferencia del operador de asignación en C y Objective-C, el operador de asignación en Swift no devuelve un valor. La siguiente instrucción no es válida:

1if x = y {
2    // Esto no es válido, porque x = y no devuelve un valor.
3}

Esta característica previene el uso accidental del operador de asignación (=) en ocasiones en las cuales, en realidad, se desea usar el operador «igual que» (==). Al hacer if x = y inválido, Swift te ayuda a evitar este tipo de errores en tu código.

Operadores aritméticos

Swift provee los cuatro operadores aritméticos estándar para todos los tipos numéricos:

  • Suma (+)
  • Resta (-)
  • Multiplicación (*)
  • División (/)
11 + 2       // igual a 3
25 - 3       // igual a 2
32 * 3       // igual a 6
410.0 / 2.5  // igual a 4.0

A diferencia de los operadores aritméticos en C y Objective-C, los operadores aritméticos en Swift no permiten, por defecto, el desborde de valores. Puedes habilitar el mecanismo de desbordamiento de valores utilizando los operadores de desbordamiento de Swift (como en a &+ b). Para más información, visita Operadores de desbordamiento.

El operador de adición también soporta la concatenación de cadenas:

1"Hola, " + "mundo"  // es igual a "Hola, mundo"

Operador de resto

El operador de resto (%) se usa para calcular el resto de la división de dos valores enteros Int. En la expresión a % b, el operador de resto calcula cuántas veces cabe b en a y devuelve el valor que queda (conocido como resto).

Nota

Al operador de resto (%) se le conoce también como operador módulo en otros lenguajes. Sin embargo, su comportamiento en Swift con número negativos significa que —estrictamente hablando—, es una operación de resto más que de tipo módulo.

Así es como funciona el operador de resto. Para calcular 9 % 4, primero se calcula cuántos 4s caben en un 9:

Graphical illustration of the remainder operation

Es posible insertar dos 4s en un 9, y resta 1 (se muestra en naranja).

En Swift, esto se escribiría como:

19 % 4    // es igual a 1

Para determinar la respuesta de a % b, el operador % calcula la siguiente ecuación y arroja resto como su valor a devolver:

        a = (b × algún multiplicador) + resto

donde algún multiplicador es el mayor número de múltiplos de b que caben en a.

Al insertar 9 y 4 en esta ecuación, se tiene que:

        9 = (4 × 2) + 1

El mismo método se aplica al calcular el resto para un valor negativo de a:

1-9 % 4   // es igual a -1

Al insertar -9 y 4 en esta ecuación, se tiene que:

        -9 = (4 × -2) + -1

lo cual da un valor residual de -1.

El signo de b es ignorado para valores negativos de b. Esto significa que la respuesta de a % b y a % -b siempre es la misma.

Operador unario de resta

El signo de un valor numérico se puede alternar con el prefijo -, conocido como el operador unario de resta:

1let tres = 3
2let menosTres = -tres       // menosTres es igual a -3
3let masTres = -menosTres    // masTres es igual a 3, o «menos menos tres»

El operador unario de resta (-) precede directamente a su operando, sin ningún espacio en blanco.

Operador unario de suma

El operador unario de suma (+) simplemente devuelve el valor de su operando, sin ningún cambio.:

1let menosSeis = -6
2let tambienMenosSeis = +menosSeis  // tambienMenosSeis es igual a -6

Aunque el operador unario de suma no hace nada como tal, puedes usarlo (con números positivos) para darle simetría a tu código cuando haces uso del operador unario de resta para números negativos.

Operadores de asignación compuesta

Al igual que C, Swift ofrece operadores de asignación compuesta, los cuales combinan asignación (=) con otra operación. Un ejemplo de esto es el operador de adición asignación (+=):

1var a = 1
2
3a += 2
4// a es ahora igual a 3

La expresión a += 2 es la forma concisa de a = a + 2. Efectivamente, la adición y la asignación se combinan en un operador que realiza ambas operaciones al mismo tiempo.

Nota

Los operadores de asignación compuesta no devuelven un valor. Por ejemplo, no puedes escribir let b = a += 2.

Para obtener información sobre los operadores proporcionados por la librería estándar de Swift, visita Declaraciones de Operadores (en inglés).

Operadores de comparación

Swift cuenta con los siguientes operadores de comparación:

  • Igual que (==)
  • No igual que (!=)
  • Mayor que (>)
  • Menor que (<)
  • Mayor o igual que (>=)
  • Menor o igual que (<=)

Nota

Swift también provee dos operadores de identidad=== y !==—, los cuales puedes usar para probar si dos referencias a objetos se refieren a la misma instancia de un objeto. Para más información, visita Operadores de identidad.

Cada uno de los operadores de comparación devuelve un valor booleano, para indicar si un enunciado es verdadero (true) o falso (false):

11 == 1   // true porque 1 es igual a 1
22 != 1   // true porque 2 no es igual a 1
32 > 1    // true porque 2 es mayor que 1
41 < 2    // true porque 1 es menor que 2
51 >= 1   // true porque 1 es mayor o igual a 1
62 <= 1   // false porque 2 no es mayor o igual a 1

Los operadores de comparación se utilizan, a menudo, en las instrucciones condicionales; como, por ejemplo, en la instrucción if:

1let nombre = "mundo"
2
3if nombre == "mundo" {
4    print("Hola, mundo.")
5} else {
6    print("Lo siento, \(nombre), pero no te reconozco.")
7}
8// Imprime "Hola, mundo." porque nombre es, en efecto, igual a "mundo".

Para más información sobre la instrucción if, visita Flujo de control.

Puedes comparar dos tuplas si estas tienen el mismo tipo y el mismo número de valores. Las tuplas se comparan de izquierda a derecha, un valor a la vez, hasta que la comparación encuentra dos valores que no sean iguales. En caso de existir, estos dos valores son comparados y el resultado de dicha comparación determinará el resultado general de la comparación de las tuplas. Si todos los elementos de ambas tuplas son iguales, entonces las tuplas son consideradas iguales. Por ejemplo:

1(1, "pantera") < (2, "manzana")   // true porque 1 es menor que 2; "pantera" y "manzana" no se comparan
2(3, "manzana") < (3, "naranja")    // true porque 3 es igual a 3, y "manzana" es menor que "naranja"
3(4, "perro") == (4, "perro")       // true porque 4 es igual a 4, y "perro" es igual a "perro"

En el ejemplo anterior, puedes notar el mecanismo de comparación de izquierda a derecha en la primera línea. Dado que 1 es menor que 2, (1, "pantera") es considerada menor que (2, "manzana"), independientemente de cualquier otro valor de las tuplas. No importa que "pantera" no sea menor que "manzana", porque la comparación ya ha sido determinada por los primeros elementos de los tuplas. Sin embargo, cuando los primeros elementos de las tuplas son iguales, sus segundos elementos son comparados; esto es lo que ocurre en las líneas segunda y tercera.

Las tuplas se pueden comparar con un determinado operador solo si dicho operador puede ser aplicado a cada valor en las respectivas tuplas. Por ejemplo, como se demuestra en el código a continuación, es posible comparar dos tuplas de tipo (String, Int) porque tanto los valores tipo String como los tipo Int pueden compararse usando el operador <. Contrario a esto, dos tuplas de tipo (String, Bool) no pueden ser comparadas usando el operador < porque este operador no puede ser usado con valores tipo Bool.

1("azul", -1) < ("morado", 1)        // OK, evalúa a true
2("azul", false) < ("morado", true)  // Error porque < no puede comparar valores booleanos

Nota

La librería estándar de Swift incluye operadores de comparación de tuplas para tuplas con menos de siete elementos. Para comparar tuplas con siete o más elementos, debes implementar los operadores de comparación tú mismo.

Operador condicional ternario

El operador condicional ternario es un operador especial, con tres operandos, el cual toma la forma pregunta ? respuesta1 : respuesta2. Es un atajo para evaluar una de las dos expresiones, basado en el hecho de que pregunta evalúe a verdadero (true) o falso (false). Si el valor de pregunta es true, el operador evalúa respuesta1 y devuelve su valor; de lo contrario, evalúa respuesta2 y devuelve su valor.

El operador condicional ternario es un atajo para el código a continuación:

1if pregunta {
2    respuesta1
3} else {
4    respuesta2
5}

Aquí hay un ejemplo, en el que se calcula la altura de la fila de una tabla. La altura de la fila debe ser 50 unidades más alta que la altura del contenido, si la fila tiene un encabezado, y 20 unidades más alta si la fila no tiene un encabezado:

1let alturaContenido = 40
2let tieneEncabezado = true
3let alturaFila = alturaContenido + (tieneEncabezado ? 50 : 20)
4// alturaFila es igual a 90

El ejemplo anterior es una forma concisa del código a continuación:

1let alturaContenido = 40
2let tieneEncabezado = true
3let alturaFila: Int
4
5if tieneEncabezado {
6    alturaFila = alturaContenido + 50
7} else {
8    alturaFila = alturaContenido + 20
9}
10// alturaFila es igual a 90

El uso del operador condicional ternario en el primer ejemplo significa que a alturaFila se le puede asignar el valor correcto en una sola línea de código, lo cual es más conciso que el código utilizado en el segundo ejemplo.

El operador condicional ternario proporciona un atajo eficiente para decidir cuál de las dos expresiones considerar. Sin embargo, debes utilizar el operador condicional ternario con cuidado. Abusar de lo conciso puede conllevar a escribir código difícil de leer. Evita combinar múltiples instancias del operador condicional ternario en un mismo enunciado compuesto.

Operador nil-coalescing

El operador nil-coalescing (a ?? b) extrae un opcional a, si este contiene un valor; o devuelve un valor predeterminado b, si a es nil. La expresión a siempre debe ser de tipo opcional. La expresión b debe coincidir con el tipo almacenado en a.

El operador nil-coalescing es una forma concisa del código a continuación:

1a != nil ? a! : b

El código anterior utiliza el operador condicional ternario y la extracción forzada (a!) para acceder al valor contenido por a cuando a no es nil, y para devolver b en el caso contrario. El operador nil-coalescing brinda una manera más elegante de encapsular esta verificación y extracción condicionales de forma más concisa y legible.

Nota

Si el valor de a no es nil, el valor de b no es evaluado. A esto se le conoce como evaluación de cortocircuito.

El siguiente ejemplo, utiliza el operador nil-coalescing para elegir entre un nombre de color predeterminado y un nombre de color opcional, definido por el usuario:

1let nombreColorPredeterminado = "rojo"
2var nombreColorDefinidoPorUsuario: String?   // resulta en nil, por defecto
3
4var nombreColorParaUsar = nombreColorDefinidoPorUsuario ?? nombreColorPredeterminado
5// nombreColorDefinidoPorUsuario es nil, por lo que a nombreColorParaUsar
6// se le asigna el valor predeterminado de "rojo"

La variable nombreColorDefinidoPorUsuario se define como un String opcional, con un valor predeterminado de nil. Como el tipo de nombreColorDefinidoPorUsuario es opcional, puedes usar el operador nil-coalescing para considerar su valor. En el ejemplo anterior, el operador es utilizado para determinar un valor inicial para una variable de tipo String llamada nombreColorParaUsar. Dado que nombreColorDefinidoPorUsuario es nil, la expresión nombreColorDefinidoPorUsuario ?? nombreColorPredeterminado devuelve el valor de nombreColorPredeterminado, es decir, "rojo".

Si asignas un valor diferente de nil a nombreColorDefinidoPorUsuario y ejecutas la verificación con el operador nil-coalescing nuevamente, el valor contenido por nombreColorDefinidoPorUsuario se usará en lugar del predeterminado:

1nombreColorDefinidoPorUsuario = "verde"
2
3nombreColorParaUsar = nombreColorDefinidoPorUsuario ?? nombreColorPredeterminado
4// nombreColorDefinidoPorUsuario no es nil, por lo que a nombreColorParaUsar
5// se le asigna "verde"

Operadores de rango

Swift incluye varios operadores de rango, que son maneras concisas de expresar un rango de valores.

Operador de rango cerrado

El operador de rango cerrado (a...b) define un rango que va de a a b, e incluye los valores a y b. El valor de a no debe ser mayor que el de b.

El operador de rango cerrado es útil al iterar sobre un rango, del cual se desean usar todos sus valores, como en el caso de un ciclo for-in:

1for index in 1...5 {
2    print("\(index) multiplicado por 5 es \(index * 5)")
3}
4// 1 multiplicado por 5 es 5
5// 2 multiplicado por 5 es 10
6// 3 multiplicado por 5 es 15
7// 4 multiplicado por 5 es 20
8// 5 multiplicado por 5 es 25

Para más información sobre los ciclos for-in, visita Flujo de control.

Operador de rango semiabierto

El operador de rango semiabierto (a..<b) define un rango que va de a a b, pero no incluye a b. Se considera semiabierto porque contiene su primer valor, pero no el valor final. Al igual que con el operador de rango cerrado, el valor de a no debe ser mayor que el de b. Si el valor de a es igual a b, entonces el rango será un rango vacío.

Los rangos semiabiertos son particularmente útiles al trabajar con listas basadas en cero, como los arrays, donde resulta conveniente contar hasta (pero sin incluir) la longitud de la lista:

1let nombres = ["Ana", "Alejandro", "Cindy", "Laura"]
2let conteo = nombres.count
3
4for i in 0..<conteo {
5    print("La persona #\(i + 1) se llama \(nombres[i])")
6}
7// La persona #1 se llama Ana
8// La persona #2 se llama Alejandro
9// La persona #3 se llama Cindy
10// La persona #4 se llama Laura

Nota que el array contiene cuatro elementos, pero 0..<conteo solo cuenta hasta 3 (el índice del último elemento en el array), porque es un rango semiabierto. Para más información sobre arrays, visita Arrays.

Rangos unilaterales

El operador de rango cerrado cuenta con una forma alternativa para los rangos que continúan lo más lejos posible en una dirección; por ejemplo, un rango que incluye todos los elementos de un array desde el índice 2 hasta el final del array. En estos casos, puedes omitir el valor en uno de los lados del operador. A este tipo de rangos se les denomina rango unilateral, porque el operador tiene un valor, solamente, en uno de los lados. Por ejemplo:

1for nombre in nombres[2...] {
2    print(nombre)
3}
4// Cindy
5// Laura
6
7for nombre in nombres[...2] {
8    print(nombre)
9}
10// Ana
11// Alejandro
12// Cindy

El operador de rango semiabierto también tiene una forma unilateral, donde solo se escribe su valor final. Al igual que cuando incluyes un valor en ambos lados, el valor final no forma parte del rango. Por ejemplo:

1for nombre in nombres[..<2] {
2    print(nombre)
3}
4// Ana
5// Alejandro

Los rangos unilaterales se pueden utilizar en otros contextos, no solo en los subscripts. No puedes iterar sobre un rango unilateral que omita un primer valor, ya que no estaría claro donde debe comenzar la iteración. Aunque sí puedes iterar en un rango unilateral que omita su valor final; sin embargo, como el rango continúa indefinidamente, asegúrate de agregar una condición final explícita para el ciclo. También puedes verificar si un rango unilateral contiene un valor en particular, como se muestra en el código a continuación.

1let rango = ...5
2
3rango.contains(7)   // false
4rango.contains(4)   // true
5rango.contains(-1)  // true

Operadores lógicos

Los operadores lógicos modifican o combinan los valores lógicos booleanos true y false. Swift provee los tres operadores lógicos básicos encontrados en lenguajes basados en C:

  • NO (NOT) lógico (!)
  • Y (AND) lógico (&&)
  • O (OR) lógico (||)

Operador lógico NO

El operador lógico NO (!) invierte un valor booleano, de manera que true se convierte en false y false se convierte en true.

El operador lógico NO es un operador prefijo, y aparece inmediatamente antes de su operando, sin ningún espacio en blanco. Se puede leer como «no a», como se observa en el siguiente ejemplo.:

1let entradaPermitida = false
2
3if !entradaPermitida {
4    print("ACCESO DENEGADO")
5}
6// Imprime "ACCESO DENEGADO"

La expresión if !entradaPermitida se puede leer como «si no entrada permitida». La línea subsiguiente solo se ejecuta si «no entrada permitida» es true; es decir, si entradaPermitida es false.

Como se aprecia en este ejemplo, la elección cuidadosa de nombres de constantes y variables booleanas puede ayudar a mantener el código legible y conciso, evitando —al mismo tiempo— negaciones dobles o enunciados lógicos confusos.

Operador lógico Y

El operador lógico Y (&&) crea expresiones lógicas, en donde ambos valores deben ser true para que toda la expresión sea true.

Si cualquiera de los dos valores es false, toda la expresión será false. De hecho, si el primer valor es false, el segundo valor ni siquiera será evaluado, ya que no será posible que este haga que la expresión sea true. Esto se conoce como evaluación cortocircuito.

Este ejemplo considera dos valores de tipo Bool y solo permite el acceso si ambos valores son true:

1let codigoPuertaIngresado = true
2let aproboEscanerDeRetina = false
3
4if codigoPuertaIngresado && aproboEscanerDeRetina {
5    print("¡Bienvenida!")
6} else {
7    print("ACCESO DENEGADO")
8}
9// Imprime "ACCESO DENEGADO"

Operador lógico O

El operador lógico O (||) es un operador interfijo formado por dos barras verticales. Se usa para crear expresiones lógicas, en las cuales solo uno de los dos valores tiene que ser true para que toda la expresión sea true.

Al igual que el operador lógico Y, el operador lógico O utiliza la evaluación cortocircuito para evaluar sus expresiones. Si el lado izquierdo de una expresión lógica O es true, el lado derecho no se evaluará, ya que no podrá cambiar el resultado de toda la expresión.

En el siguiente ejemplo, el primer valor booleano (tieneLlaveDeLaPuerta) es false, pero el segundo valor (conoceClaveMaestra) es true. Dado que un valor es true, toda la expresión evalúa a true, y se permite el acceso:

1let tieneLlaveDeLaPuerta = false
2let conoceClaveMaestra = true
3
4if tieneLlaveDeLaPuerta || conoceClaveMaestra {
5    print("¡Bienvenida!")
6} else {
7    print("ACCESO DENEGADO")
8}
9// Imprime "¡Bienvenida!"

Combinación de operadores lógicos

Puedes combinar múltiples operadores lógicos para crear expresiones compuestas más largas:

1if codigoPuertaIngresado && aproboEscanerDeRetina || tieneLlaveDeLaPuerta || conoceClaveMaestra {
2    print("¡Bienvenida!")
3} else {
4    print("ACCESO DENEGADO")
5}
6// Imprime "¡Bienvenida!"

Este ejemplo usa múltiples operadores && y || para crear una expresión compuesta más larga. Sin embargo, los operadores && y || siguen teniendo dos operandos solamente, por lo que son, en realidad, tres expresiones pequeñas unidas entre sí. El ejemplo puede leerse como:

«Si hemos ingresado el código correcto de la puerta y aprobado el escáner de retina o si tenemos una llave válida de la puerta o si conocemos la clave maestra de emergencia, entonces permítase el acceso.»

Con base en los valores de codigoPuertaIngresado, aproboEscanerDeRetina, y tieneLlaveDeLaPuerta, las dos primeras subexpresiones son false. Sin embargo, se conoce la clave maestra de emergencia, por lo tanto, toda la expresión compuesta evalúa a true.

Nota

Los operadores lógicos && y || de Swift se asocian a la izquierda, lo que significa que expresiones compuestas con múltiples operadores lógicos evalúan primero la subexpresión a la izquierda.

Paréntesis explícitos

Algunas veces resulta conveniente incluir paréntesis —aunque no sea estrictamente necesario—, para hacer que la intención de una expresión compleja sea más fácil de leer. En el ejemplo anterior (acceso a la puerta), es útil agregar paréntesis alrededor de la primera parte de la expresión compuesta para hacer que su intención sea explícita:

1if (codigoPuertaIngresado && aproboEscanerDeRetina) || tieneLlaveDeLaPuerta || conoceClaveMaestra {
2    print("¡Bienvenida!")
3} else {
4    print("ACCESO DENEGADO")
5}
6// Imprime "¡Bienvenida!"

Los paréntesis dejan en claro que los dos primeros valores se consideran como parte de un posible estado, aparte, en la lógica general. El resultado de la expresión compuesta no cambia, pero la intención general es más clara para el lector. Siempre elige la legibilidad sobre la brevedad; usa paréntesis donde estos ayuden a hacer tus intenciones claras.