1. Anterior: Capítulo 1
Kotlin: Una Guía Ilustrada • Capítulo 2

Funciones

Chapter cover image

En el último capítulo, escribimos algo de código en Kotlin para calcular la circunferencia de un círculo en particular. En este capítulo, vamos a escribir algunas funciones que harán más fácil el cálculo de la circunferencia de cualquier círculo.

Introducción a las Funciones

Como vimos en el último capítulo, calcular la circunferencia de un círculo es fácil:

Circunferencia = 2 x pi x r

Y aquí está el código Kotlin que escribimos para hacer dicho cálculo:

val pi = 3.14
var radio = 5.2
val circunferencia = 2 * pi * radio

Este código calcula la circunferencia de un círculo que tiene 5,2 de radio. Por supuesto, ¡no todos los círculos tienen un radio de 5,2! ¿Qué pasa si quisiéramos determinar la circunferencia de un círculo que tiene radio de 6,7 o 10,0?

Circles of different sizes 10.0 5.2 6.7

Bueno, podríamos escribir múltiples ecuaciones:

val pi = 3.14
var radio = 5.2 
val circunferenciaDeCírculoPequeño = 2 * pi * radio

radio = 6.7
val circunferenciaDeCírculoMediano = 2 * pi * radio
radio = 10
val circunferenciaDeCírculoGrande = 2 * pi * radio

Esto definitivamente funciona, pero – ¡mira cuantas veces tuvimos que escribir lo mismo una y otra vez!

We had to write '2 * pi * radius' multiple times

Cuando tenemos el mismo código en diferentes partes decimos que tenemos duplicación. En la mayoría de los casos código duplicado es malo porque:

  1. Cuando tipiamos lo mismo muchas veces, tarde o temprano vas a tipear mal en uno de los casos. Por ejemplo, una vez podrías tipear accidentalmente 3 * pi * radio.
  2. Si quieres cambiar la ecuación, vas a tener que buscar todos los lugares donde la escribiste y asegurarte de que actualices cada una de ellas.
  3. Puede ser más difícil para leer cuando tus ojos ven el mismo código escrito una y otra vez en la pantalla.

Vamos cambiando nuestro código de tal manera que solo veamos 2 * pi * radio una sola vez, y entonces usar eso para calcular la circunferencia de cualquier círculo. En otras palabras, ¡vamos a remover la duplicación!

Removiendo Duplicación con Funciones

Aunque escribimos 2 * pi * radio tres veces, el único valor que cambio cada vez fue el radio. En otras palabras, 2 nunca cambió, y pi nunca cambió (cada vez es igual a 3.14). Pero el valor del radio fue diferente cada vez: primero 5.2, después 6.7 y después 10.0.

Como el radio es lo único que cambia, seria genial que pudiéramos convertir el radio a una circunferencia. En otras palabras, ¿cómo sería una máquina que cuando se inserta un radio en un lado nos devuelve una circunferencia al otro lado?

A machine with 5.2 going in as the radius and 32.565 coming out the other side.
  • Como ponemos el radio de un lado, a esto le podríamos llamar la entrada.
  • Y como la circunferencia sale del otro lado, podríamos llamar esto la salida.

Ahora, no tenemos que crear una máquina, pero podemos crear una función en su lugar, la cual hará lo que exactamente queremos que haga - le daremos un radio y nos devolverá una circunferencia.

Concepto Básico de Funciones

Creando una Función

Aquí mostramos como sería escribir una simple función en Kotlin:

fun circumference(radius: Double) = 2 * pi * radius

Parece mucha información, pero hay pocas partes y son fáciles de entender.

  1. Primero, fun es una palabra reservada (como val y var son palabras reservadas). Eso le dice a Kotlin que estás escribiendo una función.
  2. A continuación, circunferencia es el nombre de la función. Podemos nombra nuestra función con cualquier nombre, pero escogí circunferencia.
  3. Después, (radio: Double) dice que la función tiene un valor de entrada llamado radio, el cual tiene un tipo llamado Double. radio es llamado también el parámetro de la función.
  4. Después, el : Double después de los paréntesis indica el tipo de salida de la función, en este caso Double. Esto es llamado el tipo de retorno de la función.
  5. Y finalmente, 2 * pi * radio, es llamado el cuerpo de la función. El resultado de esta operación será el valor de salida de la función. El valor de salida es referido como el resultado de la función. Es lo que sale de nuestra máquina. Debemos notar que la expresión que se evalúa debe tener como tipo el mismo tipo del tipo retorno. En este ejemplo, 2 * pi * radio debe evaluarse a Double o vamos a tener un error.

En la imagen siguiente comparamos nuestra función como la máquina que imaginamos arriba.

The same machine as previously with arrows pointing to the different parts of the Kotlin function. entra radio sale circunferencia fun circunferencia ( radio : Double): Double = 2 * pi * radio nombre de entrada nombre de esta máquina Cálculo que pasa adentro de esta máquina

Como recordarás en el capítulo anterior no siempre tienes que especificar el tipo de la variable, en su lugar dejar que Kotlin use su inferencia de tipos. Tú también puedes usar inferencia de tipos cuando escribes en funciones como esta. Simplemente remueve el tipo de retorno, para que se vea así:

fun circunferencia(radio: Double) = 2 * pi * radio

Programadores en Kotlin a menudo usan inferencia de tipos en funciones simple como esta.

Llamar a una Función

Cuando usas la función – es decir, cuando pones algo en la máquina, se conoce como llamar o invocar a la función. El lugar donde se llama se denomina código de llamada o lugar de llamada.

Aquí es como se llama a una función en Kotlin:

val circumferenceOfSmallCircle = circumference(5.2) val circunferenciaDelCírculoPequeño = circunferencia ( 5.2 ) variable nombre de la función argumento
  1. Primero, circunferenciaDelCírculoPequeño es una variable que va a contener el resultado de la llamada a la función (es decir, lo que sea que salga de la máquina)
  2. Después, circunferencia es el nombre de la función que estamos llamando.
  3. El valor 5.2 es el argumento de la función , es la cosa que ponemos adentro de la máquina. Cuando llamamos a la función con un argumento, a veces decimos que estamos pasando el argumento a la función.

Desde este momento en adelante, voy a incluir los paréntesis después del nombre de la función. Por ejemplo, me refiero a la función como circunferencia() en vez de solo circunferencia. Esto lo hago para que quede claro que me refiero a la función.

¿Qué pasa cuando llamas a la función? Digamos que escribimos una función y la llamamos así:

fun circunferencia(radio: Double) = 2 * pi * radio

val circunferenciaDelCírculoPequeño = circunferencia(5.2)

Cuando llamamos a la función es como que pusiéramos el cuerpo de la función 2 * pi * radio donde la función es llamada – circunferencia(5.2).

Entonces podrías imaginarlo así:

fun circunferencia(radio: Double) = 2 * pi * radio

val circunferenciaDelCírculoPequeño = = 2 * pi * radio

Luego, dado que pasamos 5.2 como radio, podríamos imaginarnos sustituyendo el valor 5.2 donde habíamos colocado el radio:

fun circunferencia(radio: Double) = 2 * pi * radio

val circunferenciaDelCírculoPequeño = = 2 * pi * 5.2

En resumen, cuando llamamos a circunferencia(5.2) es como si escribiéramos 2 * pi * 5.2 en el mismo lugar.

Ahora que la función puede calcular la circunferencia usando el radio, podemos llamar a nuestra función ¡todas las veces que necesitamos!

val pi = 3.14
fun circunferencia(radio: Double) = 2 * pi * radio

val circunferenciaDeCírculoPequeño = circunferencia(5.2)
val circunferenciaDeCírculoMediano = circunferencia(6.7)
val circunferenciaDeCírculoGrande = circunferencia(10.0)

¿No creen que se ve mejor esto que el Listado 2.2? Porque tenemos una función que no necesita escribir 2 * pi * radio una y otra vez,

Argumentos y Parámetros: ¿Cuál es la diferencia?

Es fácil confundir un argumento con un parámetro, por eso es importante entender la diferencia:

  1. Un argumento es un valor que pasamos a la función. Por ejemplo, si quieres pasar 5.2 a la función circunferencia(). El argumento es 5.2.
  2. Un parámetro es la variable adentro de la función que contendrá el valor. Por ejemplo, cuando pasamos 5.2 a circunferencia(), es asignado al parámetro llamado radio.

Un argumento es un valor que usualmente viene de afuera de la función. Como mnemónico, simplemente asocie la palabra “argumento” con la palabra “afuera”, como en la caricatura.

Funciones con más de un parámetro

La función circunferencia() tiene un solo parámetro llamado radio, pero a veces necesitas que una función tenga más de eso. Vamos a crear una función que tenga dos parámetros.

Incluso si la clase de física fue hace tiempo, probablemente sepas como calcular la velocidad. Es fácil de recordar porque lo decimos todo el tiempo: “El límite de velocidad es 100 km por hora”.

“Kilómetros por hora” es distancia (“kilómetros”) dividido para (“por”) tiempo (“hora”).

speed equals distance divided by time velocidad = distancia tiempo

“Kilómetros por hora” es distancia (“kilómetros”) dividido para (“por”) tiempo (“hora”).

distance sliding off of time, resulting in a forward slash velocidad = distancia tiempo velocidad = distancia / tiempo velocidad  = distancia tiempo

Para empezar, escribamos un código simple que calcule la velocidad promedio de un carro que ha viajado 321,8 kilómetros en 3,15 horas.

val distancia = 321.8
val tiempo = 4.15
val velocidad = distancia / tiempo

Ahora, convirtamos la expresión distancia / tiempo en una función. Para crear una para la velocidad, necesitamos dos cosas: distancia y tiempo.

En Kotlin, cuando necesitas una función con dos parámetros, puedes sepáralos con una coma. Por ejemplo, podemos llamar a la función velocidad con los valores que usamos en el código de arriba, separado cada valor con una coma.

fun velocidad(distancia: Double, tiempo: Double) = distancia / tiempo

Cuando necesites llama a esta función, los argumentos están separados por una coma. Por ejemplo, podemos llamar a la función velocidad() con los mismos valores que usamos anteriormente, separando cada valor con una coma:

val velocidadPromedio = velocidad(321.8, 4.15)

El resultado es aproximadamente 77.54 kilómetros por hora.

Notemos que los argumentos que están aquí están en el mismo orden que los parámetros.

  • Como 321.8 es el primer argumento, 321.8 va a ser asignado al primer parámetro que es la distancia.
  • Como 4.15 es el segundo argumento, 4.15 será asignado a el segundo para parámetro, que es tiempo.

En otras palabras, la posición de los argumentos importa cuando llamamos a la función de esta manera, por lo cual a veces llamamos a los argumentos como estos argumentos posicionales.

First argument is assigned to first parameter. Second argument is assigned to second parameter. fun velocidad (distancia: Double, tiempo: Double) = distancia / tiempo fun velocidadPromedio = velocidad ( 321.8 , 4.15 )

¡Pero esto no es la única manera de pasar argumentos a una función!

Argumentos Nombrados

En lugar de confiar en la posición de los argumentos, puedes usar el nombre de los parámetros, así:

val velocidadPromedio = velocidad(distancia = 321.8, tiempo = 4.15)

Estos son llamados argumentos nombrados. Lo genial de los argumentos nombrados es que el orden no importa. Por eso podrías llamar a la función con los argumentos en diferente orden:

val velocidadPromedio = velocidad(tiempo = 4.15, distancia = 321.8)

En otras palabras, todas estas cinco llamadas de funciones terminarán con el mismo resultado:

val velocidadPromedio1 = velocidad (321.8, 4.15)
val velocidadPromedio2 = velocidad (distancia = 321.8, 4.15)
val velocidadPromedio3 = velocidad (321.8, tiempo = 4.15)
val velocidadPromedio4 = velocidad (distancia = 321.8, tiempo = 4.15)
val velocidadPromedio5 = velocidad (tiempo = 4.15, distancia = 321.8)

Argumentos Predeterminados

En algunos casos, tu querrás pasar el mismo valor de un argumento a una función varias veces. Por ejemplo, si quieres calcular que tan rápido:

  1. Una persona camina
  2. Otra persona usa una bicicleta
  3. Un tercera persona está manejando un auto, y
  4. Una cuarta persona está volando en un avión.
Cartoon silhouettes of a walker, a biker, an automobile, and an airplane.

Cada uno estaba moviendo por 2 horas, excepto por el avión el cual llego a su destino en 1,5 horas. Usando la función velocidad() que usamos antes, puedes calcular la velocidad así:

val velocidadCaminando = velocidad(10.2, 2.0)
val velocidadEnBicicleta = velocidad(29.6, 2.0)
val velocidadManejando = velocidad(225.3, 2.0)
val velocidadVolando = velocidad(1368.747, 1.5)

En vez de pasar el valor 2.0 para el parámetro del tiempo una y otra vez, podemos usar 2.0 como un argumento predeterminado cuando definimos la función.

Actualicemos nuestra función velocidad() para el parámetro tiempo tenga un valor predeterminado de 2.0.

fun velocidad(distancia: Double, tiempo: Double = 2.0) = distancia / tiempo

Ahora podemos omitir el argumento del tiempo cuando el valor es igual a 2.0 así:

val velocidadCaminando = velocidad(10.2)
val velocidadEnBicicleta = velocidad(29.6)
val velocidadManejando = velocidad(225.3)
val velocidadVolando = velocidad(1368.747, 1.5)

Para caminar, andar en bicicleta, y manejar vamos a dejar la función sin el argumento tiempo, así el valor predeterminado es 2.0. Pero para volar, nosotros pasamos el valor 1.5. Los resultados del listado 2.16 y el listado 2.14 son los mismos.

Cuando un argumento predeterminado viene primero

Nuestro caminador, ciclista, conductor y piloto están de nuevo en ello. ¡Pero esta vez es una carrera! Quien llegue primero a la meta a 42,195 kilómetros de distancia se lleva el premio.

Todos terminaron la carrera con excepción del avión, el cual tuvo se le pinchó una rueda antes de despegar.

val velocidadCaminando = velocidad(42.195, 8.27)
val velocidadEnBicicleta = velocidad(42.195, 2.85)
val velocidadManejando = velocidad(42.195, 0.37)
val velocidadVolando = velocidad(0.12, 0.01)

Como la distancia es 42.195 para todos excepto el avión, actualicemos nuestro código – removemos el valor predeterminado por el tiempo y añadimos un valor predeterminado a la distancia de 42.195.

fun velocidad(distancia: Double = 42.195, tiempo: Double) = distancia / tiempo

Ahora, como el caminador, el ciclista y el conductor viajan la misma distancia, podemos omitir el valor del primer parámetro, distancia. Podría ser tentador llamar a la función de la siguiente manera:

val velocidadCaminando = velocidad(8.27)
val velocidadEnBicicleta = velocidad(2.85)
val velocidadManejando = velocidad(0.37)
val velocidadVolando = velocidad(0.12, 0.01)
Error

Pero esto causaría un error: No se pasó ningún valor para el parámetro ’tiempo’ (No value passed for parameter ’tiempo’.). ¿Por qué paso esto?

Como usamos argumentos posicionales, al final terminamos omitiendo tiempo en vez de distancia.

En otras palabras, queríamos asignar 8.27 a el parámetro tiempo, así:

We wanted 8.27 to be assigned to time. fun velocidad (distancia: Double = 42.195 , tiempo: Double) = distancia / tiempo val velocidadCaminando = velocidad ( 8.27 )

Pero en realidad asignamos 8.27 al parámetro distancia, porque 8.27 es el primer argumento, y distancia es el primer parámetro.

We actually assigned 8.27 to distance. fun velocidad (distancia: Double = 42.195 , tiempo: Double) = distancia / tiempo val velocidadCaminando = velocidad ( 8.27 )

Para decirle a Kotlin que queremos enviar el tiempo, simplemente usamos los argumentos nombrados de la siguiente manera:

val velocidadCaminando = velocidad(tiempo = 8.27)
val velocidadEnBicicleta = velocidad(tiempo = 2.85)
val velocidadManejando = velocidad(tiempo = 0.37)
val velocidadVolando = velocidad(0.12, 0.01)

Ahora, el primer parámetro, distancia, será predeterminado a 42.195 cuando llamemos a velocidad() para caminar, andar en bicicleta y manejar.

Cuerpos de Expresión y Cuerpos de Bloque

Hasta ahora hemos escrito funciones que solo evalúan una expresión.

  • La función circunferencia() solo evalúa 2 * pi * radio
  • La función velocidad() solo evalúa distancia / tiempo

Veamos nuestro código de circunferencia() de nuevo.

val pi = 3.14

fun circunferencia(radio: Double) = 2 * pi * radio

Cuando escribimos la función de esta manera, el cuerpo es solamente una expresión singular, podríamos decir que la función tiene un cuerpo de expresión.

Kotlin también nos da una segunda forma para escribir funciones. Re-escribamos la función circunferencia() usando esta segunda forma:

val pi = 3.14

fun circunferencia(radio: Double): Double {
	return 2 * pi * radio
}

Cuando escribimos una función de esta manera, decimos que la función tiene un cuerpo de bloque.

Esta forma en la cual una función con cuerpo de bloque se escribe es un poco más compleja que la forma en que se escribe funciones con cuerpo de expresión, así que veamos esta nueva forma más de cerca.

Primero, notemos el : Double al final de los paréntesis. Podíamos usar inferencia de tipo con cuerpo de expresión, pero no podemos usarlo con cuerpo de bloque, debemos especificar el tipo de retorno explícitamente.

Después, notemos las llaves de apertura y cierre: { y }. Todo lo que está escrito entre estas dos llaves es referido como bloque de código (Por lo cual nos referimos a esta función como una función con código de bloque).

Finalmente, notemos la palabra return adentro del bloque de código. La palabra return es una palabra reservada que le dice a Kotlin que la expresión que sigue es lo que la función va a retornar. Como en las funciones con cuerpo de expresión, el valor de la función que debe retornar debe ser del mismo tipo que el que la función dice que retorna.

Whatever is returned by the function (e.g., 2 * pi * radius) must match the return type specified after the right parenthesis (e.g., Double). fun circunferencia (radio: Double): Double { return 2 * pi * radio } lo que sea que se devuelva aquí debe coincidir con el tipo indicado aquí

Funciones con cuerpo de bloque toman más tiempo para escribir que funciones con cuerpo de expresión, pero entonces ¿por qué querríamos usarlas?

  • Ellas te dejan escribir más de una línea en la función.
  • Ellas te dejan escribir sentencias adentro de las funciones, no solamente expresiones.

Por ejemplo, hasta ahora hemos definido pi fuera de nuestra función. Pero sería genial definirla adentro de la función:

fun circunferencia(radio: Double) : Double {
	val pi = 3.14
	return 2 * pi * radio
}

Moviendo pi adentro de la función hará que solo sea accesible adentro de la función. En otras palabras, si tratas de usar pi fuera de la función, vas a tener un error.

fun circunferencia(radio: Double) : Double {
	val pi = 3.14
	return 2 * pi * radio
}

val tau = 2 * pi
Error

A lo largo de este libro, veremos muchos ejemplos de ambas funciones con cuerpos de expresión y funciones con cuerpos de bloque.

Funciones sin un Resultado

Hay a veces que quisieras una función que no retorne un resultado. Por ejemplo, digamos que tenemos una variable que contiene un número de incrementos a través del tiempo, llamado contador. Crearemos la función llamada incremento(). Cada vez que llamaos a esta función, se incrementará el contador por uno, escribiendo la sentencia: contador = contador + 1.

Funciones con cuerpos de expresión pueden solo contener una sola expresión. No podemos usar una sentencia en un cuerpo de expresión. Por ejemplo, no podemos hacer esto:

var contador = 0

fun incremento() = contador = contador + 1
Error

En vez de usar cuerpo de expresión, es mejor usar cuerpo de bloque para esta función.

var contador = 0

fun incremento() {
	contador = contador + 1
}

Probablemente notaste que no especificamos el tipo de retorno.

There's no return type specified in this function: fun increment() { counter = counter + 1 } fun incremento () { contador = contador + 1 } No hay tipo de retorno aquí

Estarías sorprendido en aprender que, aunque no especificamos el tipo, e incluso esta función no contiene ninguna expresión, está función aun retorna un valor.

¡Así es! Cuando omites el tipo de retorno en una función que tiene cuerpo de bloque, automáticamente retorna un tipo especial de Kotlin llamado Unit.

Unit – No es un numero o cadena, y no podemos hacer mucho con él. Pero es a veces útil en algunos casos que vamos a explorar cuando lleguemos a genéricos en un capítulo futuro.

Pero por ahora ¡concluyamos el capítulo!

Resumen

En este capítulo aprendimos:

A continuación, en capitulo 3, aprenderemos todo acerca condicionales en Kotlin, así podemos hacer nuestro código diferentes cosas en diferentes situaciones.

Share this article:

  • Share on Twitter
  • Share on Facebook
  • Share on Reddit