官术网_书友最值得收藏!

Local functions

The idea behind functions is very simple – split up a large program into smaller chunks that can be reasoned more easily and allow the reuse of the code to avoid repetition. This second point is known as the Don't Repeat Yourself (DRY) principle. The more times you write the same code, the higher the chances of a bug creeping in.

When this principle is taken to its logical conclusion, you will have created a program that consists of many small functions, each doing a single thing; this is similar to the Unix principle of using small programs, where each program does a single job.

The same principle applies to the code inside a function. Typically, in Java, for example, a large function or method might be broken down by calling several support functions declared in either the same class or a helper class that contains static methods.

Kotlin allows us to take this a step further by supporting functions declared inside other functions. These are called local or nested functions. Functions can even be nested multiple times.

The earlier example of printing areas can be written in the following style:

    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") 
    } 

As you can see, the calculateArea function is now inside printArea and is therefore not accessible to the code outside. This is useful when we want to hide functions that are used as implementation details of a larger function. We could also achieve a similar effect by defining a member function as private. So, do local functions have any other advantages? Yes, they do! Local functions can access the parameters and variables defined in the outer scope:

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

You will notice that we've removed the parameters from the calculateArea function, and it now directly uses the parameters defined in the enclosing scope. This makes the nested function more readable and saves us repeating the parameter definitions, which is very useful for functions with many parameters.

Let's work through an example of a function that could be broken down using local functions:

    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) 
      } 
    } 

This is the well-known Fizz Buzz problem. This asks you to print out the integers from the start to the end value. However, if the integer is a multiple of 3, you should print Fizz. If it is a multiple of 5, you should print Buzz. If it is a multiple of 3 and 5, then print Fizz Buzz together.

The first solution is short and readable, but it duplicates some code. The modulo checks are coded twice, which doubles the potential for a bug. Clearly, this example is extremely simple, so the chances of a typo are minimal; however, it serves to demonstrate the issue for larger problems.

We can declare a local function for each of the modulo checks so that we only have to code it once. This brings us to the next iteration of our solution:

    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) 
      } 
    }

Here, our if...else branches now invoke the nested isFizz and isBuzz functions. However, it is still a bit verbose to pass k to the function each time. Is there a way that we can avoid this? It turns out that the answer is yes! We can define local functions not just directly inside other functions, but also in any for loops, while loops, and other blocks:

    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) 
      } 
    } 

In this third iteration of our function, we have moved the function definitions inside the for loop. We can now omit the parameter declarations and access k directly.

Finally, we could take advantage of the when statement introduced in Chapter 2, Kotlin Basics, to remove some of the noise of the if…else keywords:

    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) 
        } 
      } 
    } 

This gives us our final solution, which avoids repetition of code and is more readable than the initial iteration.

主站蜘蛛池模板: 新源县| 措美县| 富顺县| 岳西县| 沧源| 宜川县| 鹤庆县| 涟源市| 庄河市| 宁南县| 兰西县| 高台县| 安徽省| 吉木乃县| 米易县| 瑞安市| 瑞昌市| 于田县| 福州市| 龙陵县| 广灵县| 龙州县| 南涧| 邵武市| 乃东县| 内丘县| 新郑市| 萨迦县| 平昌县| 亳州市| 陆良县| 阳新县| 陆良县| 潢川县| 丹寨县| 孝义市| 德江县| 丹巴县| 沂水县| 邵武市| 修水县|