Funciones y parámetros
Al terminar: Escribir funciones con parámetros, valor de retorno y argumentos por nombre, y la diferencia entre devolver e imprimir.
Al terminar sabrás escribir una función (un bloque de código con nombre que recibe datos y devuelve un resultado) y llamarla. Con eso tendrás el primer trozo del torneo en código: encabezadoDeJornada(numero, totalDeJornadas), que escribe un texto como «Jornada 2 de 5».
Importa porque cualquier programa, del tamaño que sea, está hecho de funciones pequeñas que se llaman unas a otras.
Temas: funciones, parámetros, valor de retorno, valores por defecto y argumentos con nombre. En la lección siguiente, Condiciones: if y when, harás que el programa decida con preguntas de sí o no.
Qué es una función
Hasta ahora todo tu código ha ido dentro de main, que ya es una función. Una función es un bloque de instrucciones con nombre: lo escribes una vez y lo usas todas las veces que quieras, sin copiarlo. Sirve para dos cosas: no repetir código y poner nombre a una idea («calcular el área», «saludar») para que el programa se lea como una frase.
Dos palabras que vas a usar mucho: llamar a una función es pedirle que se ejecute (escribes su nombre y, entre paréntesis, los argumentos), y devolver es entregar un resultado a quien la llamó. Ya has llamado a una: println("Hola") es una llamada a la función println con el argumento "Hola".
// Una función es un bloque de instrucciones con nombre: se escribe una vez y se usa las veces que haga falta
fun saludar(nombre: String) { // nombre es un parámetro: el hueco que se rellena al llamarla
println("Hola, $nombre") // $nombre mete el valor del parámetro en el texto
}
// Esta devuelve un resultado: «: Int» dice de qué tipo es, y return lo entrega
fun areaDelRectangulo(ancho: Int, alto: Int): Int {
val area = ancho * alto // variable local: se crea aquí dentro y solo se puede usar dentro de esta función
return area // return entrega el resultado a quien la llamó y termina la función
}
fun main() {
saludar("Ana") // llamar a la función: su nombre y, entre paréntesis, el argumento ("Ana")
saludar("Luis") // la misma función con otro argumento
val area = areaDelRectangulo(3, 4) // el resultado devuelto se puede guardar en una variable
println(area) // 12
println(areaDelRectangulo(5, 2)) // 10: o usarlo directamente
println("El área es ${areaDelRectangulo(3, 4)} y la mitad es ${area / 2}") // ${…} mete en el texto el resultado de una cuenta o de una llamada
}Hola, Ana
Hola, Luis
12
10
El área es 12 y la mitad es 6Dentro de un texto, $nombre mete el valor de una variable. Si lo que quieres meter no es un nombre solo sino una cuenta o una llamada a una función, la escribes entre llaves: ${area / 2}. Lo que haya dentro de las llaves se calcula y su resultado pasa a formar parte del texto.
return entrega el resultado de la función a quien la llamó y la termina en ese punto: lo que haya debajo ya no se ejecuta.
Fíjate en area, dentro de areaDelRectangulo: es una variable local, una variable que se crea dentro de la función y solo existe mientras la función se ejecuta. Fuera de ella no se puede usar (en main hay otra area, distinta, que guarda el resultado).
Devolver no es imprimir
Es la confusión más común al empezar. println muestra algo en la pantalla; return entrega un valor al código que llamó a la función, que puede guardarlo, sumarlo o usarlo en otra cuenta. Una función puede imprimir sin devolver nada, y puede devolver sin imprimir nada:
fun imprimirDoble(x: Int) { // imprime el doble, pero no devuelve nada
println(x * 2)
}
fun doble(x: Int): Int { // devuelve el doble, pero no imprime nada
return x * 2
}
fun main() {
imprimirDoble(4) // se ve 8 en pantalla, pero ese 8 no se puede guardar ni usar después
val resultado = doble(4) // aquí no se ve nada: el 8 queda guardado en la variable
println(resultado + 1) // 9: se puede seguir usando
}8
9Una función que no devuelve nada, como saludar o imprimirDoble, en realidad devuelve Unit, el «nada» de Kotlin, y no hace falta escribirlo ni poner return.
Funciones de una línea, valores por defecto y argumentos con nombre
Tres comodidades que verás en todo el torneo:
- Función de expresión. Una expresión es un trozo de código que da un valor, como
x * 2(ya la conoces de Variables, tipos y operaciones). Si el cuerpo de la función es una sola, puedes quitar las llaves y elreturn: después del=va la expresión y su valor es lo que devuelve la función. El tipo de retorno se deduce solo, aunque puedes escribirlo igualmente (fun doble(x: Int): Int = x * 2); cuando el cuerpo es largo conviene escribirlo para que se lea mejor. - Valor por defecto. Un parámetro puede traer un valor ya puesto, como la «sal al gusto» de la receta: si al llamar no dices nada, se usa ese; si quieres otro, lo indicas.
- Argumento con nombre. Al llamar, escribes
prefijo = "Aviso"en lugar de pasar solo el valor. La llamada se lee sin tener que adivinar qué es cada dato, y puedes darlos en cualquier orden.
// De expresión: sin llaves ni return; después del = va una expresión y su valor es el resultado
fun doble(x: Int) = x * 2
// Con valor por defecto: si no pasas el prefijo, se usa "Nota"
fun etiqueta(texto: String, prefijo: String = "Nota") = "$prefijo: $texto"
fun main() {
println(doble(5)) // 10
println(etiqueta("traer agua")) // sin prefijo: usa el de por defecto
println(etiqueta("traer agua", "Aviso")) // con prefijo, por posición
println(etiqueta(prefijo = "Aviso", texto = "traer agua")) // con nombre, en cualquier orden
}10
Nota: traer agua
Aviso: traer agua
Aviso: traer aguaResumen de las formas de escribir una función que ya has visto:
| Detalle | Cómo se escribe | Para qué |
|---|---|---|
| Suelta | fun escrita directamente en el fichero, como main, fuera de cualquier otra cosa | Una función que no depende de nada más |
| De bloque | fun area(a: Int, b: Int): Int { return a * b } | Varias líneas; el tipo de retorno se escribe tras : y se devuelve con return |
Sin resultado (Unit) | fun saludar(n: String) { println(n) } | Solo hace algo; no devuelve nada |
| Valor por defecto | prefijo: String = "Jornada" | Que el parámetro se pueda omitir |
| Argumento con nombre | prefijo = "Fecha" | Leer la llamada sin adivinar |
| De expresión | fun doble(x: Int) = x * 2 | Cuerpo de una sola expresión; el tipo de retorno se infiere (o se escribe tras :) |
Reto · Ejercicio: la taquilla
En el Kotlin Playground (el mismo de Tu primer proyecto en Kotlin; no toques tu proyecto), escribe dos funciones. precioFinal recibe las entradas, el precio de cada una (si no se dice, 10) y el porcentaje de descuento (si no se dice, 0), y devuelve lo que se paga. Pista: primero multiplica entradas por precio y por 100 - descuento, y al final divide entre 100, para que la división entera no recorte antes de tiempo. mostrar recibe lo mismo, no devuelve nada e imprime «N entradas: » seguido del precio final. En main, llama a mostrar para 2 entradas, para 5 entradas con un 10 de descuento y para 5 entradas a precio 4, pasando el descuento y el precio por su nombre. La consola debe mostrar:
2 entradas: 20
5 entradas: 45
5 entradas: 20Ver solución
// De expresión, con dos valores por defecto. Primero se multiplica y al final se divide
fun precioFinal(entradas: Int, precio: Int = 10, descuento: Int = 0): Int =
entradas * precio * (100 - descuento) / 100
// Sin resultado (Unit): imprime, no devuelve nada
fun mostrar(entradas: Int, precio: Int = 10, descuento: Int = 0) {
println("$entradas entradas: ${precioFinal(entradas, precio, descuento)}") // ${…}: el resultado de una llamada, dentro del texto
}
fun main() {
mostrar(2) // usa el precio y el descuento por defecto
mostrar(5, descuento = 10) // el descuento, por su nombre
mostrar(5, precio = 4) // el precio, por su nombre
}Paso · Puntos.kt y un Main.kt que lo usa
Objetivo: dos ficheros. Puntos.kt con el encabezado de jornada y un Main.kt que lo llama. Al terminar, ./gradlew :torneo:run imprime el nombre del torneo y los encabezados de sus jornadas. La regla de los puntos la añades en la lección siguiente.
1 · Crea Puntos.kt. En la vista Android, despliega torneo → kotlin+java. Haz clic derecho sobre el paquete com.sebastiannarvaez.marcador.torneo (donde está Main.kt) → New → Kotlin Class/File → File → escribe Puntos y pulsa Intro. Se abre Puntos.kt con la línea package ya escrita.
Piezas que vas a usar, todas de esta lección:
fun nombre(a: Int, b: Int): String = …: función de expresión; lo que va tras el=es lo que devuelve.prefijo: String = "Jornada": parámetro con valor por defecto.numero = 1: argumento con nombre.
Intenta escribirlo tú, en dos ficheros:
Puntos.kt:encabezadoDeJornada(numero, totalDeJornadas, prefijo)devuelve un texto como «Jornada 2 de 5»; el prefijo es opcional y, si no se da, es «Jornada».Main.kt, que ya existe: sustituye todo su contenido. El programa presenta el torneo («Copa Barrio»), escribe el encabezado de las jornadas 1, 2 y 3 de 3 (con argumentos con nombre) y uno más con el prefijo «Fecha».
Al ejecutar ./gradlew :torneo:run debe verse:
Torneo: Copa Barrio
Jornada 1 de 3
Jornada 2 de 3
Jornada 3 de 3
Fecha 1 de 3El título de cada bloque de abajo es la ruta del fichero en tu proyecto. En cada uno, selecciona todo (Cmd + A) y pega el bloque.
Intenta primero. Cuando termines, compara con la solución.Mostrar el código
package com.sebastiannarvaez.marcador.torneo
// Función suelta: escrita directamente en el fichero.
// Parámetros con valor por defecto: se pueden omitir al llamar.
fun encabezadoDeJornada(numero: Int, totalDeJornadas: Int, prefijo: String = "Jornada"): String =
"$prefijo $numero de $totalDeJornadas"package com.sebastiannarvaez.marcador.torneo
// Punto de entrada de consola: ./gradlew :torneo:run
fun main() {
val nombreDelTorneo = "Copa Barrio"
val totalDeJornadas = 3
println("Torneo: $nombreDelTorneo")
// Argumento con nombre: se lee sin adivinar qué es cada número.
println(encabezadoDeJornada(numero = 1, totalDeJornadas = totalDeJornadas))
println(encabezadoDeJornada(numero = 2, totalDeJornadas = totalDeJornadas))
println(encabezadoDeJornada(numero = 3, totalDeJornadas = totalDeJornadas))
println(encabezadoDeJornada(1, 3, prefijo = "Fecha"))
}Lo que ves en Puntos.kt: encabezadoDeJornada es una función suelta y de expresión, con un parámetro opcional. Los argumentos con nombre de Main.kt (numero = 1) se leen sin abrir la función.
Punto de control
- En la vista Android, el paquete
com.sebastiannarvaez.marcador.torneodetorneo → kotlin+javacontiene ahoraMain.ktyPuntos.kt. ./gradlew :torneo:runimprime las cinco líneas de arriba, deTorneo: Copa BarrioaFecha 1 de 3.Puntos.kttiene una función de expresión con un valor por defecto. Todavía no hay tests (comprobaciones automáticas que escribirás en dos lecciones, en Bucles y tu primer test): no hay nada que contar hasta entonces.
Comprueba lo que entendiste
4 preguntas · necesitas 3 para completar la lección
1.Con
fun encabezado(numero: Int, total: Int, prefijo: String = "Jornada"), ¿cuál de estas llamadas es válida?2.Una función
fun mostrarDoble(x: Int) { println(x * 2) }imprime el doble. ¿Qué pasa conval d = mostrarDoble(4)?3.Con
fun doble(x: Int) = x * 2, ¿qué muestraprintln(doble(5))?4.¿Qué hace
returndentro de una función?