Las funciones son bloques de código delimitadas por llaves ({) e identificadas por un nombre. A diferencia de los bloques de código usados en los bucles y condicionales, no hay que satisfacer ninguna condición en las funciones para funcionar, sino que las líneas de código de la función se ejecutan siempre que se la invoca.
Una vez que se llama una función, la ejecución del programa continúa dentro de la misma y, cuando termina, regresa a la línea de código que la invocó para continuar con la ejecución anterior.
func f1() { // Declaración y Definición
let x = 1 + 2 // 2
}
f1() // Invocación
var counter = 0
while counter < 3 { // Invocando tres veces
f1()
counter += 1
}
La ventaja de una función es que podemos llamarla desde cualquier parte y siempre ejecutará la misma operación. No se requiere saber cómo funciona internamente, solo hay que pasarle los valores deseados y leer el resultado.
Parámetros
Se puede pasar valores a una función por medio de parámetros dentro de paréntesis. Los parámetros se definen como constantes separadas por comas, así: nombre1: Tipo1, nombre2: Tipo2, .... Cuando se llama la función, los parámetros se vuelven constantes dentro del contexto del bloque de la función.
func f2(a: Int, b: Int) {
let x = a + b
}
f2(a: 1, b: 2) // provoca que x = 1 + 2
f2(a: 2, b: 5) // provoca que x = 2 + 5
Etiquetas de los argumentos
Cada vez que se llama una función con parámetros se debe poner el nombre de cada parámetro antes del valor del argumento. Estas so las etiquetas de los argumentos.
A veces el nombre del parámetro es claro para el contexto al interior de la función pero para la lectura del llamado. Por esta razón, se puede definir la etiqueta de cada argumento de forma explícita, declarándola antes del nombre del parámetro separados por un espacio.
func f2(first a: Int, second b: Int) {
let x = a + b
}
f2(first: 1, second: 2) // provoca que x = 1 + 2
Notar que dentro de la función se usan los parámetros a y b, y fuera se ven las etiquetas first y second.
Si, en cambio, se quiere omitir la etiqueta, se puede reemplazar por un guión bajo (_).
func f2(first a: Int, _ b: Int) {
let x = a + b
}
f2(first: 1, 2) // provoca que x = 1 + 2
Parámetro por referencia inout
Usualmente los argumentos de una función son copiados como constantes en los parámetros de la misma. Sin embargo, se puede pasar una referencia a una variable como argumento a la función para modificar esa variable dentro de la misma. Para esto se debe marcar el parámetro como inout.
func f4() {
var counter = 0
f5(a: &counter)
print(counter) // 1
}
func f5(a: inout Int) {
a += 1
}
f4() // Imprime: 1
Notar que:
- No se puede pasar un valor escalar como argumento. Se necesita pasar una variable que se pueda modificar. En caso de que se trate de pasar un escalar, se tendrá el error: Cannot pass immutable value as inout argument: literals are not mutable.
- Se usa el operador
¶ extraer la referencia a la variable. Si se olvida, se tendrá el error: Passing value of type 'Int' to an inout parameter requires explicit '&'
Valor por defecto de un parámetro
En la declaración de la función, después del tipo de un parámetro, se puede anexar un argumento por defecto con la sintaxis nombre: Tipo = valor_por_defecto. Así, se puede omitir pasar un valor para este parámetro.
func f2(first a: Int, second b: Int = 1) {
let x = a + b
}
f2(first: 3) // provoca que x = 3 + 1
Valor de retorno
Los valores definidos dentro de una función no pueden ser accedidos por fuera de ella. Se podría decir que están atrapados. Para poder comunicar un resultado con el resto del programa, una función puede retornar un valor con la expresión return que, al llamarse, termina la ejecución de la función.
Para retornar un valor se debe cambiar la firma de la función, especificando el tipo del valor de retorno (e.g. -> Tipo).
func f2(a: Int, b: Int) -> Int {
return a + b
}
let x1 = f2(a: 1, b: 2) // provoca que x1 = 3
let x2 = f2(a: 2, b: 5) // provoca que x2 = 7
Si la función solo tiene una instrucción que es retornar un valor, se puede omitir la palabra clave return.
func f3(a: Int, b: Int) -> Int {
a + b
}
Sobrecarga de funciones
No se puede crear más de una función con la mismo nombre; nombre, tipo y número de parámetros. Sin embargo, se puede crear más de una función con el mismo nombre y variar sus parámetros: en tipo y cantidad. A esto se le conoce como Sobrecarga de funciones.
func f(value: Int) -> Int {
value + 1
}
func f(value: String) -> Int {
value.count
}
print(f(value: "Hola")) // Infiere que usa f(value: String)
print(f(value: 1)) // Infiere que usa f(value: Int)
También se puede sobrecargar el tipo del valor de retorno, sin embargo, si se deja la puerta abierta para que el compilador de Swift infiera el tipo de función que debe ejecutar, se tendrá el error: Ambiguous use of '...'. Por ejemplo:
func f(value: Int) -> Int {
value + 1
}
func f(value: Int) -> Double {
Double(value) + 1.0
}
let result = f(value: 1) // Ambiguous use of 'f(value:)
print(result)
Para resolver este problema, hay que suministrar un poco más de información que le permita al compilador deducir cuál función se debe usar. Por ejemplo:
func f(value: Int) -> Int {
value + 1
}
func f(value: Int) -> Double {
Double(value) + 1.0
}
let result: Double = f(value: 1) // ✅
print(result)
Funciones genéricas
Consideremos el escenario de dos funciones con el mismo nombre, el mismo valor de retorno y un solo parámetro, solo que sería de un tipo diferente en cada función. Luego, el sistema seleccionará la función a ejecutar dependiendo del tipo del argumento.
func f(value: Int) -> String {
"Valor: \(value)"
}
func f(value: String) -> String {
"Valor: \(value)"
}
let result1 = f(value: 1)
let result2 = f(value: "Hola")
En el ejemplo anterior, las dos funciones tenían el mismo cuerpo. En este caso, podemos evitar duplicar código haciendo una función genérica:
func f<T>(value: T) -> String {
"Valor: \(value)"
}
let result1 = f(value: 1) // Valor: 1
let result2 = f(value: "Hola") // Valor: Hola
Un tipo genérico funciona como plantilla. Cuando se invoca la función, esta plantilla se convierte en el tipo de valor recibido.
Puede haber más de un tipo de dato plantilla, por ejemplo:
func f<T, U>(value1: T, value2: U) -> String {
"Valores: \(value1) \(value2)"
}
let result1 = f(value1: 1, value2: 0.5) // Valores: 1 0.5
let result2 = f(value1: "Hola", value2: "Chao") // Valores: Hola Chao
Biblioteca estándar
La biblioteca estándar de Swift incluye varios operadores, tipos primitivos y funciones predefinidas como:
-
print(String): Imprime un string en la consola. -
abs(Int): Retorna el valor absoluto de un entero. -
max(Values): Retorna el valor más grande -
min(Values): Retorna el valor más pequeño.
Funciones para detener la ejecución del programa
-
fatalError(String): Detiene la ejecución de la aplicación e imprime un mensaje en la consola. -
precondition(Bool, String): Detiene la ejecución de la aplicación si la condición esfalse, e imprime un mensaje en consola.
Funciones para crear colecciones
-
stride(from: Value, through: Value, by: Value): Retorna una colección de valores desdefromhastathrough(inclusive) en intervalos deby. -
stride(from: Value, to: Value, by: Value): Retorna una colección de valores desdefromhasta antes dethrough(excluyéndolo) en intervalos deby. -
repeatElement(Value, count: Int): Retorna una colección repitiendocountveces el valorValue. -
zip(Collection, Collection): Retorna una colección de tuplas con los valores de los argumentos en orden secuencial.
Alcance de una función (Scope)
Un bloque de código se define como un conjunto de variables e instrucciones envueltas entre llaves (i.e. { ... }). Las variables declaradas dentro de un bloque no pueden ser accedidas desde otro bloque diferente. La región del código que tiene acceso a estas variables se conoce como "Alcance" o "Scope" del bloque.
Las variables definidas en un scope global pueden ser accedidas desde un scope local, pero no al revés.
Top comments (0)