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:
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?
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!
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:
- 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
.- 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.
- 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 elradio
. En otras palabras,2
nunca cambió, ypi
nunca cambió (cada vez es igual a3.14
). Pero el valor delradio
fue diferente cada vez: primero5.2
, después6.7
y después10.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?
- 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:
Parece mucha información, pero hay pocas partes y son fáciles de entender.
- Primero,
fun
es una palabra reservada (comoval
yvar
son palabras reservadas). Eso le dice a Kotlin que estás escribiendo una función.- A continuación,
circunferencia
es el nombre de la función. Podemos nombra nuestra función con cualquier nombre, pero escogícircunferencia
.- Después, (
radio: Double)
dice que la función tiene un valor de entrada llamadoradio
, el cual tiene un tipo llamadoDouble
.radio
es llamado también el parámetro de la función.- Después, el
: Double
después de los paréntesis indica el tipo de salida de la función, en este casoDouble
. Esto es llamado el tipo de retorno de la función.- 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 aDouble
o vamos a tener un error.En la imagen siguiente comparamos nuestra función como la máquina que imaginamos arriba.
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:
- 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)- Después,
circunferencia
es el nombre de la función que estamos llamando.- 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 solocircunferencia
. 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 valor5.2
donde habíamos colocado elradio
: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éramos2 * 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:
- Un argumento es un valor que pasamos a la función. Por ejemplo, si quieres pasar
5.2
a la funcióncircunferencia()
. El argumento es5.2
.- Un parámetro es la variable adentro de la función que contendrá el valor. Por ejemplo, cuando pasamos
5.2
acircunferencia()
, es asignado al parámetro llamadoradio
.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 llamadoradio
, 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”).
“Kilómetros por hora” es distancia (“kilómetros”) dividido para (“por”) tiempo (“hora”).
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
ytiempo
.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 ladistancia
.- Como
4.15
es el segundo argumento,4.15
será asignado a el segundo para parámetro, que estiempo
.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.
¡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:
- Una persona camina
- Otra persona usa una bicicleta
- Un tercera persona está manejando un auto, y
- Una cuarta persona está volando en un avión.
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 deltiempo
una y otra vez, podemos usar2.0
como un argumento predeterminado cuando definimos la función.Actualicemos nuestra función
velocidad()
para el parámetrotiempo
tenga un valor predeterminado de2.0
.fun velocidad(distancia: Double, tiempo: Double = 2.0) = distancia / tiempo
Ahora podemos omitir el argumento del
tiempo
cuando el valor es igual a2.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 es2.0
. Pero para volar, nosotros pasamos el valor1.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 ladistancia
de42.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)
ErrorPero 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ámetrotiempo
, así:Pero en realidad asignamos
8.27
al parámetrodistancia
, porque8.27
es el primer argumento, ydistancia
es el primer parámetro.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 a42.195
cuando llamemos avelocidad()
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úa2 * pi * radio
- La función
velocidad()
solo evalúadistancia / 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 palabrareturn
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.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
ErrorA 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 llamadaincremento()
. Cada vez que llamaos a esta función, se incrementará elcontador
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
ErrorEn 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.
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:
- Que es una función es y cómo nos ayuda a remover código duplicado.
- Que es un parámetro en una función y como difiere de un argumento de una llamada de función.
- La diferencia entre argumentos posicional o nombrados.
- Como darles a tus argumentos valores predeterminados.
- La diferencia entre cuerpo de expresión o cuerpo de bloque.
- Como Kotlin podría usar el tipo
Unit
si no tenemos un resultado que retornar en una función.A continuación, en capitulo 3, aprenderemos todo acerca condicionales en Kotlin, así podemos hacer nuestro código diferentes cosas en diferentes situaciones.