Funciones locales en Kotlin

La idea detrás de las funciones es muy simple: dividir un programa grande en fragmentos más pequeños que puedan razonarse más fácilmente y permitan la reutilización del código para evitar repeticiones. Este segundo punto se conoce como el principio SECO: No te repitas. Cuantas más veces escriba el mismo código, mayores serán las posibilidades de que se produzca un error. Cuando este principio se lleva a su conclusión lógica, habrá creado un programa que consta de muchas funciones pequeñas, cada una de las cuales realiza una función. una cosa; esto es similar al principio de Unix de los programas pequeños, donde cada programa hace un solo trabajo. El mismo principio se aplica al código dentro de una función. Por lo general, en Java, por ejemplo, una función o método grande puede desglosarse llamando a varias funciones de soporte declaradas en la misma clase o en una clase auxiliar que contiene métodos estáticos.

Ejemplo

Kotlin nos permite llevar esto un paso más allá al admitir funciones declaradas dentro de otras funciones. Estas se denominan funciones locales o anidadas. Las funciones incluso se pueden anidar varias veces. El ejemplo de áreas de impresión se puede escribir en el siguiente estilo:

Kotlin

fun printArea(width: Int, height: Int): Unit {
  fun calculateArea(width: Int, height: Int): Int = width * height
  val area = calculateArea(width, height)
  println("The area is $area")
}

Como puede ver, la función de cálculo de área ahora está dentro de área de impresión y, por lo tanto, no es accesible para el código externo. Esto es útil cuando queremos ocultar funciones que solo se usan como detalles de implementación de una función más grande. También podríamos lograr un efecto similar definiendo una función miembro como privada. Entonces, ¿las funciones locales tienen otras ventajas? ¡Ellos si! Las funciones locales pueden acceder a los parámetros y variables definidos en el ámbito externo:

Kotlin

fun printArea2(width: Int, height: Int): Unit {
  fun calculateArea(): Int = width * height
  val area = calculateArea()
  println("The area is $area")
}

Tenga en cuenta que hemos eliminado los parámetros de la función de cálculo del área y ahora utiliza directamente los parámetros definidos en el ámbito adjunto. Esto hace que la función anidada sea más legible y evita tener que repetir las definiciones de los parámetros, lo cual es muy útil para funciones con muchos parámetros. Analicemos un ejemplo de una función que podría desglosarse usando funciones locales:

Kotlin

fun fizzbuzz(start: Int, end: Int): Unit {
  for (k in start..end) {
    if (k % 3 == 0 && k % 5 == 0)
        println("Fizz Buzz")
    else if (k % 3 == 0)
        println("Fizz")
    else if (k % 5 == 0)
        println("Buzz")
    else
        println(k)
  }
}

Este es el conocido problema de Fizz Buzz. El requisito le pide que imprima los números enteros desde el valor inicial hasta el final. Sin embargo, si el número entero es un múltiplo de 3, debe imprimir Fizz. Si es un múltiplo de 5, debe imprimir Buzz. Si es un múltiplo de 3 y 5, entonces imprima Fizz Buzz juntos.

La primera solución es corta y legible, pero duplica parte del código. Las comprobaciones del módulo se codifican dos veces, lo que duplica la posibilidad de que se produzca un error. Claramente, este ejemplo es extremadamente simple, por lo que las posibilidades de un error tipográfico son mínimas; sin embargo, sirve para demostrar el problema para problemas más grandes. Podemos declarar una función local para cada una de las comprobaciones de módulo para que solo tengamos que codificarla una vez. Esto nos lleva a la siguiente iteración de nuestra solución:

Kotlin

fun fizzbuzz2(start: Int, end: Int): Unit {
  fun isFizz(k: Int): Boolean = k % 3 == 0
  fun isBuzz(k: Int): Boolean = k % 5 == 0
  for (k in start..end) {
    if (isFizz(k) && isBuzz(k))
        println("Fizz Buzz")
    else if (isFizz(k))
        println("Fizz")
    else if (isBuzz(k))
        println("Buzz")
    else
        println(k)
  }
}

Aquí, nuestras ramas if…else ahora invocan las funciones anidadas isFizz e isBuzz. Sin embargo, todavía es un poco detallado pasar k a la función cada vez. ¿Hay alguna manera de que podamos evitar esto? Resulta que la respuesta es ¡sí! Podemos definir funciones locales no solo directamente dentro de otras funciones, sino también en bucles for, bucles while y otros bloques:

Kotlin

fun fizzbuzz3(start: Int, end: Int): Unit {
  for (k in start..end) {
    fun isFizz(): Boolean = k % 3 == 0
    fun isBuzz(): Boolean = k % 5 == 0
      
    if (isFizz() && isBuzz())
        println("Fizz Buzz")
    else if (isFizz())
        println("Fizz")
    else if (isBuzz())
        println("Buzz")
    else
        println(k)
  }
}

En esta tercera iteración de nuestra función, hemos movido las definiciones de función dentro del bucle for. Así que ahora podemos omitir las declaraciones de parámetros y acceder a k directamente. Finalmente, podríamos aprovechar los conceptos básicos de Kotlin para eliminar parte del ruido de las palabras clave if…else:

Kotlin

fun fizzbuzz4(start: Int, end: Int): Unit {
    for (k in start..end) {
      fun isFizz(): Boolean = k % 3 == 0
      fun isBuzz(): Boolean = k % 5 == 0
    when {
      isFizz() && isBuzz() -> println("Fizz Buzz")
      isFizz() -> println("Fizz")
      isBuzz() -> println("Buzz")
      else -> println(k)
      }
  }
}

Esto nos da nuestra solución final, que evita la repetición de código y es más legible que la iteración inicial.

Publicación traducida automáticamente

Artículo escrito por eralokyadav2019 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *