- Learn Kotlin Programming(Second Edition)
- Stephen Samuel Stefan Bocutiu
- 861字
- 2021-06-24 14:13:35
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.
- Dynamics 365 Application Development
- Eclipse Plug-in Development:Beginner's Guide(Second Edition)
- Mastering Python Networking
- 快速念咒:MySQL入門指南與進階實戰
- Learning DHTMLX Suite UI
- Learning SciPy for Numerical and Scientific Computing(Second Edition)
- Android開發案例教程與項目實戰(在線實驗+在線自測)
- Getting Started with Greenplum for Big Data Analytics
- C#應用程序設計教程
- C++從入門到精通(第5版)
- Arduino可穿戴設備開發
- PowerDesigner 16 從入門到精通
- OpenCV 3.0 Computer Vision with Java
- Python全棧開發:基礎入門
- After Effects CC案例設計與經典插件(視頻教學版)