Operadores Básicos
Realiza operaciones como asignación, aritmética, y comparación.
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
— y el operador lógico AND (&&
) combina dos valores booleanos —como en if ingresoCodigoPuerta && aproboEscanerDeRetina
—.
Swift soporta los operadores que puede que ya conozcas de lenguajes como C, y mejora muchos aspectos con el fin de eliminar errores comunes de codificación. El operador de asignación (=
) no devuelve un valor; esto, con el fin de evitar que se utilice erróneamente cuando lo que se pretende es usar el operador «igual que» (==
). Los operadores aritméticos (+
, -
, *
, /
, %
, y demás) detectan e impiden 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 provee «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 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 sobre 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 enc!
). - Los operadores binarios operan sobre dos elementos (como en
2 + 3
) y son interfijos, ya que aparecen entre sus dos objetivos. - Los operadores ternarios operan sobre 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
:
let b = 10
var a = 5
a = b
// ahora, a es 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:
let (x, y) = (1, 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 de Swift no devuelve un valor. La siguiente instrucción no es válida:
if x = y {
// Esto no es válido, porque x = y no devuelve un valor.
}
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 soporta los cuatro operadores aritméticos estándar para todos los tipos numéricos:
- Suma (
+
) - Resta (
-
) - Multiplicación (
*
) - División (
/
)
1 + 2 // es igual a 3
5 - 3 // es igual a 2
2 * 3 // es igual a 6
10.0 / 2.5 // es igual a 4.0
A diferencia de los operadores aritméticos en C y Objective-C, los operadores aritméticos de 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:
"Hola, " + "mundo" // es igual a "Hola, mundo"
Operador de residuo
El operador de residuo (%
) se usa para calcular el residuo de la división de dos valores enteros Int
. En la expresión a % b
, el operador de residuo calcula cuántas veces cabe b
en a
y devuelve el valor restante (conocido como residuo).
Así es como funciona el operador de residuo. Para calcular 9 % 4
, primero se calcula cuántos 4
s caben en un 9
:
Es posible insertar dos 4
s en un 9
, y el restante es 1
(la franja color anaranjado).
En Swift, esto se escribiría como:
9 % 4 // es igual a 1
Para determinar la respuesta de a % b
, el operador %
calcula la siguiente ecuación y arroja residuo
como su valor a devolver:
a
= (b
× algún multiplicador
) + residuo
donde algún multiplicador
es el número mayor 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 residuo para un valor negativo de a
:
-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:
let tres = 3
let menosTres = -tres // menosTres es igual a -3
let 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:
let menosSeis = -6
let tambienMenosSeis = +menosSeis // tambienMenosSeis es igual a -6
Aunque el operador unario de suma no hace nada como tal, puedes usarlo para darle simetría a tu código (para los números positivos) cuando también usas el 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 (+=
):
var a = 1
a += 2
// ahora, a es 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 único operador que realiza ambas operaciones al mismo tiempo.
Para obtener información sobre los operadores proporcionados por la librería estándar de Swift, visita Declaraciones de Operadores (opens in a new tab) (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 (
<=
)
Cada uno de los operadores de comparación devuelve un valor booleano, para indicar si un enunciado es verdadero (true
) o falso (false
):
1 == 1 // true porque 1 es igual a 1
2 != 1 // true porque 2 no es igual a 1
2 > 1 // true porque 2 es mayor que 1
1 < 2 // true porque 1 es menor que 2
1 >= 1 // true porque 1 es mayor o igual a 1
2 <= 1 // false porque 2 no es menor 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
:
let nombre = "mundo"
if nombre == "mundo" {
print("Hola, mundo.")
} else {
print("Lo siento, \(nombre), pero no te reconozco.")
}
// 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 tal caso, esos 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, "pantera") < (2, "manzana") // true porque 1 es menor que 2; "pantera" y "manzana" no se comparan
(3, "manzana") < (3, "naranja") // true porque 3 es igual a 3, y "manzana" es menor que "naranja"
(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 sí son comparados; esto es lo que ocurre en las líneas segunda y tercera.
Las tuplas solo pueden ser comparadas mediante un determinado operador si dicho operador puede operar sobre cada valor de las respectivas tuplas. Por ejemplo, como se demuestra en el código a continuación, resulta posible comparar dos tuplas de tipo (String, Int)
porque tanto los valores de tipo String
como los de tipo Int
pueden ser comparados mediante el operador <
. Contrario a esto, dos tuplas de tipo (String, Bool)
no pueden compararse mediante el operador <
porque dicho operador no puede operar sobre valores de tipo Bool
.
("azul", -1) < ("morado", 1) // OK, evalúa a true
("azul", false) < ("morado", true) // Error porque < no puede comparar valores booleanos
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 dependiendo si pregunta
es verdadero o falso. Si el valor de pregunta
es true
, el operador evalúa respuesta1
y devuelve su valor. De lo contrario, el operador evalúa respuesta2
y devuelve su valor.
El operador condicional ternario es un atajo para el código a continuación:
if pregunta {
respuesta1
} else {
respuesta2
}
Acá tenemos 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 de su contenido, si la fila tiene un encabezado, y 20 unidades más alta si la fila no tiene un encabezado:
let alturaContenido = 40
let tieneEncabezado = true
let alturaFila = alturaContenido + (tieneEncabezado ? 50 : 20)
// alturaFila es igual a 90
El ejemplo anterior es una forma concisa del código a continuación:
let alturaContenido = 40
let tieneEncabezado = true
let alturaFila: Int
if tieneEncabezado {
alturaFila = alturaContenido + 50
} else {
alturaFila = alturaContenido + 20
}
// 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 resultar en un 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:
a != 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 concisa y legible.
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:
let nombreColorPredeterminado = "rojo"
var nombreColorDefinidoPorUsuario: String? // por defecto, resulta en nil
var nombreColorAUsar = nombreColorDefinidoPorUsuario ?? nombreColorPredeterminado
// nombreColorDefinidoPorUsuario es nil, por lo que a nombreColorAUsar
// 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 nombreColorAUsar
. Dado que nombreColorDefinidoPorUsuario
es nil
, la expresión nombreColorDefinidoPorUsuario ?? nombreColorPredeterminado
devuelve el valor de nombreColorPredeterminado
, es decir, "rojo"
.
Si a nombreColorDefinidoPorUsuario
le asignas un valor diferente a nil
y ejecutas la verificación con el operador nil-coalescing nuevamente, el valor contenido por nombreColorDefinidoPorUsuario
se usará en lugar del predeterminado:
nombreColorDefinidoPorUsuario = "verde"
nombreColorAUsar = nombreColorDefinidoPorUsuario ?? nombreColorPredeterminado
// nombreColorDefinidoPorUsuario no es nil, por lo que a nombreColorAUsar
// se le asigna "verde"
Operadores de rango
Swift incluye muchos operadores de rango, los cuales 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
:
for indice in 1...5 {
print("\(indice) multiplicado por 5 es \(indice * 5)")
}
// 1 multiplicado por 5 es 5
// 2 multiplicado por 5 es 10
// 3 multiplicado por 5 es 15
// 4 multiplicado por 5 es 20
// 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 que no incluye a b
. Se considera semiabierto porque contiene su primer valor, pero no a su 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:
let nombres = ["Ana", "Alejandro", "Cindy", "Laura"]
let conteo = nombres.count
for i in 0..<conteo {
print("La persona #\(i + 1) se llama \(nombres[i])")
}
// La persona #1 se llama Ana
// La persona #2 se llama Alejandro
// La persona #3 se llama Cindy
// La persona #4 se llama Laura
Observa 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 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 rangos unilaterales, porque el operador tiene un valor, solamente, en uno de los lados. Por ejemplo:
for nombre in nombres[2...] {
print(nombre)
}
// Cindy
// Laura
for nombre in nombres[...2] {
print(nombre)
}
// Ana
// Alejandro
// 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:
for nombre in nombres[..<2] {
print(nombre)
}
// Ana
// 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.
let rango = ...5
rango.contains(7) // false
rango.contains(4) // true
rango.contains(-1) // true
Operadores lógicos
Los operadores lógicos modifican o combinan los valores lógicos booleanos true
y false
. Swift soporta 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.:
let entradaPermitida = false
if !entradaPermitida {
print("ACCESO DENEGADO")
}
// 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
:
let ingresoCodigoPuerta = true
let aproboEscanerDeRetina = false
if ingresoCodigoPuerta && aproboEscanerDeRetina {
print("¡Bienvenida!")
} else {
print("ACCESO DENEGADO")
}
// 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:
let tieneLlaveDeLaPuerta = false
let conoceClaveMaestra = true
if tieneLlaveDeLaPuerta || conoceClaveMaestra {
print("¡Bienvenida!")
} else {
print("ACCESO DENEGADO")
}
// Imprime "¡Bienvenida!"
Combinación de operadores lógicos
Puedes combinar múltiples operadores lógicos para crear expresiones compuestas más largas:
if ingresoCodigoPuerta && aproboEscanerDeRetina || tieneLlaveDeLaPuerta || conoceClaveMaestra {
print("¡Bienvenida!")
} else {
print("ACCESO DENEGADO")
}
// 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 ingresoCodigoPuerta
, 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
.
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:
if (ingresoCodigoPuerta && aproboEscanerDeRetina) || tieneLlaveDeLaPuerta || conoceClaveMaestra {
print("¡Bienvenida!")
} else {
print("ACCESO DENEGADO")
}
// 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.