Swift desde Cero. Parámetros de Función, Tipos, y Anidamiento
() translation by (you can also view the original English article)
En el artículo anterior, exploramos los básicos de las funciones en Swift. Las funciones, sin embargo, tienen mucho más que ofrecer. En este artículo, continuaremos nuestra exploración de funciones y veremos los parámetros de funciones, anidamiento y tipos.
1. Nombres Locales y Externos de Parámetro
Nombres de Parámetro Locales
Revisitemos uno de los ejemplos del artículo anterior. La función printMessage
define un parámetro, message
.
1 |
func printMessage(message: String) { |
2 |
println(message) |
3 |
}
|
Incluso aunque le demos un nombre al parámetro, message
, no usamos el nombre cuando llamamos la función. Pasamos el valor para el parámetro message
.
1 |
printMessage("Hello, world!") |
El nombre que definimos en la definición de la función es un nombre de parámetro local. El valor del parámetro solo puede ser referenciado por este nombre en el cuerpo de la función. Los parámetros de función, sin embargo, son un poco más flexibles que eso. Déjame explicar a qué me refiero con eso.
Objective-C es conocido por sus largos nombres de método. Mientras que esto podría lucir torpe y poco elegante para los foráneos, hace a los métodos fáciles de entender y, si se eligen bien, muy descriptivos. Si piensas que perdiste este beneficio cuando te cambiaste a Swift, entonces te llevarás una sorpresa.
Nombres de Parámetro Externo
Cuando una función acepta varios parámetros, no siempre es obvio cuál argumento corresponde a cuál parámetro. Echa un vistazo al siguiente ejemplo para entender mejor el problema.
1 |
func power(a: Int, b: Int) -> Int { |
2 |
var result = a |
3 |
|
4 |
for _ in 1..<b { |
5 |
result = result * a |
6 |
}
|
7 |
|
8 |
return result |
9 |
}
|
La función power
eleva el valor de a
al exponente b
. Ambos parámetros son de tipo Int
. Mientras que la mayoría de la gente pasará intuitivamente el valor base como el primer argumento y el exponente como el segundo argumento, esto no es claro desde el tipo, nombre o firma de la función. Como hemos visto en el articulo anterior, invocar la función es sencillo.
1 |
power(2, 3) |
Para evitar confusión, podemos dar a los parámetros de una función nombres externos. Podemos entonces usar nombres externos cuando la función es llamada para indicar de manera ambigua cuál argumento corresponde a cuál parámetro. Echa un vistazo al ejemplo actualizado abajo.
1 |
func power(base a: Int, exponent b: Int) -> Int { |
2 |
var result = a |
3 |
|
4 |
for _ in 1..<b { |
5 |
result = result * a |
6 |
}
|
7 |
|
8 |
return result |
9 |
}
|
Nota que el cuerpo de la función no ha cambiado ya que los nombres locales no han cambiado. Sin embargo, cuando invocamos a la función actualizada, la diferencia es clara y el resultado es mucho menos confuso.
1 |
power(base: 2, exponent: 3) |
Mientras que los tipos de ambas funciones son el mismo, (Int, Int) - > Int
, las funciones son diferentes. En otras palabras, la segunda función no es una re-declaración de la primera función. La sintaxis para invocar a la segunda función podría recordarles a algunos de ustedes a Objective-C. No solo los argumentos están claramente descritos, la combinación de nombres de función y parámetros describen el propósito de la función.
En algunos casos, quieres usar los mismos nombres para nombres de parámetros locales y externos. Esto es posible y no hay necesidad de teclear el nombre del parámetro dos veces. En el siguiente ejemplo, usamos base
y exponent
como los nombres locales y externos de parámetro.
1 |
func power(#base: Int, #exponent: Int) -> Int { |
2 |
var result = base |
3 |
|
4 |
for _ in 1..<exponent { |
5 |
result = result * base |
6 |
}
|
7 |
|
8 |
return result |
9 |
}
|
Prefijando el nombre de parámetro con un símbolo #
, el nombre de parámetro sirve como el nombre local y externo del parámetro. Esto también significa que necesitamos actualizar el cuerpo de la función.
Es importante notar que al proporcionar un nombre externo para un parámetro se te pide usar ese nombre cuando invocas la función. Esto nos lleva a los valores por defecto.
Valores Por Defecto
Cubrimos los valores de parámetro por defecto en el artículo anterior, pero hay un comportamiento por defecto importante del que debes estar consciente. Si defines un valor por defecto para un parámetro, Swift automáticamente asigna un nombre externo de parámetro al parámetro. Veamos un ejemplo del articulo anterior.
1 |
func printDate(date: NSDate, format: String = "YY/MM/dd") -> String { |
2 |
let dateFormatter = NSDateFormatter() |
3 |
dateFormatter.dateFormat = format |
4 |
return dateFormatter.stringFromDate(date) |
5 |
}
|
Debido a que el segundo parámetro, format
, tiene un valor por defecto, Swift automáticamente establece el nombre externo de parámetro de format
a format
. En otras palabras, el resultado es el mismo que si prefijaramos format
con un símbolo #
. Puedes probar esto invocando la función de arriba en tu playground. ¿Qué sucede si pasas el formato sin usar el nombre de parámetro externo del segundo parámetro? La respuesta es mostrada abajo.



Swift es bastante claro sobre lo que deberíamos hacer. Para resumir, cuando defines un parámetro como opcional, Swift automáticamente establece el nombre de parámetro externo al nombre local de parámetro. La idea detrás de este comportamiento es evitar confusión y ambigüedad.
Mientras que este comportamiento es una buena mejor práctica, es posible deshabilitarlo. En la definición actualizada de función abajo, agregamos un guión bajo en donde normalmente agregaríamos el nombre externo de parámetro. Esto le dice a Swift que no queremos establecer un nombre externo de parámetro para ese parámetro en particular, a pesar de tener un valor por defecto.
1 |
func formatDate(date: NSDate, _ format: String = "YY/MM/dd") -> String { |
2 |
let dateFormatter = NSDateFormatter() |
3 |
dateFormatter.dateFormat = format |
4 |
return dateFormatter.stringFromDate(date) |
5 |
}
|
Ahora podemos invocar la función formatDate
sin proporcionar un nombre para el segundo argumento.
1 |
formatDate(NSDate(), "dd/MM/YY") |
2. Parámetros y Multabilidad
Revisitemos el primer ejemplo de este tutorial, la función printMessage
. ¿Qué sucede si cambiamos el valor del parámetro message
dentro del cuerpo de la función?
1 |
func printMessage(message: String) { |
2 |
message = "Print: \(message)" |
3 |
println(message) |
4 |
}
|
No pasa mucho para que Swift comience a quejarse.



Por defecto, los parámetros de una función son constantes. En otras palabras, mientras podemos acceder a los valores de parámetros de función, no podemos cambiar su valor. Para cambiar este comportamiento por defecto, agrega la palabra clave var
al nombre del parámetro en la definición de la función. Swift entonces creará una copia variable del valor del parámetro para que trabajes con ella en el cuerpo de la función.
1 |
func printMessage(var message: String) { |
2 |
message = "Print: \(message)" |
3 |
println(message) |
4 |
}
|
Nota que esto no significa que puedas pasar un valor, modificarlo en la función, y usar el valor modificado después de que la función ha terminado su trabajo. Swift crea una copia del valor del parámetro que solo existe para el tiempo de vida del llamado de la función. Esto está ilustrado también en el siguiente bloque de código en el cuál pasamos una constante a la función printMessage
.
1 |
func printMessage(var message: String) { |
2 |
message = "Print: \(message)" |
3 |
println(message) |
4 |
}
|
5 |
|
6 |
let myMessage = "test" |
7 |
|
8 |
printMessage(myMessage) |
3. Parámetros Variadic
Mientras el término podría sonar extraño al principio, los parámetros variadic son comunes en programación. Un parámetro variadic es un parámetro que acepta cero o más valores. Los valores necesitan ser del mismo tipo. Usad parámetros variadic en Swift es trivial como ilustra el siguiente ejemplo.
1 |
func sum(args: Int...) -> Int { |
2 |
var result = 0 |
3 |
|
4 |
for a in args { |
5 |
result += a |
6 |
}
|
7 |
|
8 |
return result |
9 |
}
|
La sintaxis es fácil de entender. Para marcar un parámetro como variadic, agregas tres puntos al tipo del parámetro. En el cuerpo de la función, el parámetro variadic es accesible como un arreglo. En el ejemplo de arriba, args
es un arreglo de valores Int
.
Debido a que Swift necesita saber qué argumentos corresponden a qué parámetros, una parámetro variadic es requerido para ser el último parámetro. También implica que una función puede tener a lo mucho un parámetro variadic.
Lo anterior también aplica si una función tiene parámetros con valores por defecto. El parámetro variadic debería siempre ser el último parámetro.
4. Parámetros Entrada-Salida
Anteriormente en este tutorial, aprendiste cómo puedes definir la mutabilidad de un parámetro usando la palabra clave var
. En esa sección, enfaticé que el valor de un parámetro variable es solo accesible desde dentro del cuerpo de la función. Si quieres pasar un valor a una función, modifícalo en la función, y pasarlo de vuelta a la función, los parámetros entrada-salida son lo que estás buscando.
El siguiente ejemplo muestra un ejemplo de cómo funcionan los parámetros entrada-salida en Swift y cómo luce la sintaxis.
1 |
func prependString(inout a: String, withString b: String) { |
2 |
a = b + a |
3 |
}
|
Hemos definido el primer parámetro como un parámetro entrada-salida agregando la palabra clave inout
. El segundo parámetro es un parámetro regular con un nombre externo de withString
y un nombre local de b
. Veamos cómo invocamos esta función.
1 |
var world = "world" |
2 |
|
3 |
prependString(&world, withString: "Hello, ") |
Declaramos una variable, world
, de tipo String
y la pasamos a la función perpendString
. El segundo parámetro es una cadena literal. Invocando la función, el valor de la variable world
se vuelve Hello, world
. Nota que el primer argumento es prefijado con un ampersand, &
, para indicar que es un parámetro in-out.
Está de más decir que las contantes y literales no pueden ser pasadas como parámetros entrada-salida. Swift arrojará un error cuando lo haces como se ilustra en la siguiente captura.



Es evidente que los parámetros entrada salida no pueden tener valores por defecto, sean variadic, o definidas como var
o let
. Si olvidas estos detalles, Swift te recodará cordialmente con un error.
5. Anidamiento
En C y Objective-C, las funciones y métodos no pueden ser anidados. En Swift, sin embargo, las funciones anidadas son bastante comunes. Las funciones que hemos visto en este y el artículo anterior son ejemplos de funciones globales, están definidas en el alcance global.
Cuando definimos una función dentro de una función global, nos referimos a esa función como una función anidada. Una función anidada tiene acceso a los valores definidos en su función envolvente. Echa un vistazo al siguiente ejemplo para entender mejor esto.
1 |
func printMessage(message: String) { |
2 |
let a = "hello world" |
3 |
|
4 |
func printHelloWorld() { |
5 |
println(a) |
6 |
}
|
7 |
}
|
Mientras que las funciones en este ejemplo no son terriblemente útiles, el ejemplo ilustra la idea de función anidada y capturar valores. La función printHelloWorld
solo es accesible desde dentro de la función printMessage
. Como se ilustra en el ejemplo, la función printHelloWorld
tiene acceso a la constante a
. El valor es capturado por la función anidada y por lo tanto es accesible desde dentro de esa función. Swift se ocupa de capturar valores, incluyendo administrar la memoria de esos valores.
6. Tipos de Funciones
En el artículo anterior, tocamos brevemente los tipos de funciones. Una función tiene un tipo particular, compuesto de los tipos de parámetro de la función y su tipo de devolución. La función printMessage
, por ejemplo, es de tipo (String) -> ()
. Recuerda que ()
simboliza Void
, que es equivalente a una tupla vacía.
Debido a que cada función tiene un tipo, es posible definir una función que acepta otra función como parámetro. El siguiente ejemplo muestra cómo funciona esto.
1 |
func printMessageWithFunction(message: String, printFunction: (String) -> ()) { |
2 |
printFunction(message) |
3 |
}
|
4 |
|
5 |
let myMessage = "Hello, world!" |
6 |
|
7 |
printMessageWithFunction(myMessage, printMessage) |
La función printMessageWithFunction
acepta una cadena como su primer parámetro un y una función de tipo (String) -> ()
como su segundo parámetro. En el cuerpo de la función, la función que pasamos es invocada con el argumento message
.
El ejemplo también ilustra cómo podemos invocar la función printMessageWithFunction
. La constante myMessage
es pasada como el primer argumento y la función printMessage
, que definimos anteriormente, como segundo argumento. ¿Qué tan interesante es eso?
Como mencioné anteriormente, también es posible devolver una función desde una función. El siguiente ejemplo es un poco inventado, pero ilustra como luce la sintaxis.
1 |
func compute(addition: Bool) -> (Int, Int) -> Int { |
2 |
func add(a: Int, b: Int) -> Int { |
3 |
return a + b |
4 |
}
|
5 |
|
6 |
func subtract(a: Int, b: Int) -> Int { |
7 |
return a - b |
8 |
}
|
9 |
|
10 |
if addition { |
11 |
return add |
12 |
} else { |
13 |
return subtract |
14 |
}
|
15 |
}
|
16 |
|
17 |
let computeFunction = compute(true) |
18 |
let result = computeFunction(1, 2) |
19 |
println(result) |
La función compute
acepta un booleano y devuelve una función de tipo (Int, Int) -> Int
. La función compute
contiene dos funciones anidadas que también son de tipo (Int, Int) -> Int
, add
y subtract
.
La función compute
devuelve una referencia a la función add
o substract
, basado en el valor del parámetro addition
. El ejemplo también muestra cómo usar la función compute
. Almacenamos la referencia a la función que es devuelta por la función compute
en la constante computeFunction
. Después invocamos la función almacenada en computeFunction
, pasando 1
y 2
, almacenando el resultado en result
, e imprimiendo el valor de result
en la salida estándar. El ejemplo podría lucir complejo, pero en realidad es fácil de entender si sabes qué está pasando.
Conclusión
Deberías ahora tener un buen entendimiento de cómo trabajan las funciones en Swift y qué puedes hacer con ellas. Las funciones son un tema común en Swift y la usarás de manera extensiva cuando trabajes con Swift.
En el siguiente artículo, nos sumergimos en cierres, una construcción poderosa que se parece mucho a los bloques en C y Objective-C, cierres en JavaScript, y lambdas en Ruby.