Roughly cover the materials in Chapters 4 and 5. ## Why Functions + Consider the following code: ```kotlin import java.util.Scanner fun main() { val scanner = Scanner(System.`in`) print("What is your score? ") val yourScore = scanner.nextInt() val yourGPA = when (yourScore) { in 90..100 -> 4.3 in 85..89 -> 4.0 in 80..84 -> 3.7 in 77..79 -> 3.3 in 73..76 -> 3.0 in 70..72 -> 2.7 in 67..69 -> 2.3 in 63..66 -> 2.0 in 60..62 -> 1.7 in 50..59 -> 1.0 else -> 0.0 } print("What is his score? ") val hisScore = scanner.nextInt() val hisGPA = when (hisScore) { in 90..100 -> 4.3 in 85..89 -> 4.0 in 80..84 -> 3.7 in 77..79 -> 3.3 in 73..76 -> 3.0 in 70..72 -> 2.7 in 67..69 -> 2.3 in 63..66 -> 2.0 in 60..62 -> 1.7 in 50..59 -> 1.0 else -> 0.0 } when { yourGPA > hisGPA -> println("Your GPA is better than his.") yourGPA < hisGPA -> println("His GPA is better than yours.") else -> println("Your GPA is equal to his.") } } ``` + Can we simplify the main function for readability? + Reuse the `when` expression + Functions are reusable protion of code + With proper use, functions make your code more readable. ## Extracting Code to Functions + ```kotlin= import java.util.Scanner fun main() { val scanner = Scanner(System.`in`) print("What is your score? ") val yourScore = scanner.nextInt() val yourGPA = scoreToGPA(yourScore) print("What is his score? ") val hisScore = scanner.nextInt() val hisGPA = scoreToGPA(hisScore) when { yourGPA > hisGPA -> println("Your GPA is better than his.") yourGPA < hisGPA -> println("His GPA is better than yours.") else -> println("Your GPA is equal to his.") } } private fun scoreToGPA(score: Int): Double { val yourGPA = when (score) { in 90..100 -> 4.3 in 85..89 -> 4.0 in 80..84 -> 3.7 in 77..79 -> 3.3 in 73..76 -> 3.0 in 70..72 -> 2.7 in 67..69 -> 2.3 in 63..66 -> 2.0 in 60..62 -> 1.7 in 50..59 -> 1.0 else -> 0.0 } return yourGPA } ``` ### Refactor to Function 1. Select the part of code ![](https://i.imgur.com/5zHJinJ.png) 2. Right click -> Refactor -> Function ![](https://i.imgur.com/kcJ0Srq.png) 3. Give proper names to function and parameters ![](https://i.imgur.com/FkHRdE1.png) 4. IntelliJ detects duplicates ![](https://i.imgur.com/s2Ls1LW.png) ## Header, Body, Scope ![](https://i.imgur.com/hRvqou0.png) ### Header + `private`: Visibility modifier + `private` means "can only be seen in the same file". + `public` means "can be seen everywhere" and is the default. When you ignore the visibility modifier, it is equivalent to use `public`. + We shall discuss other visibility modifiers later. + `fun`: function declaration keyword + Like `var` and `val` for variables and values + `scoreToGPA`: function name + `(score: Int)`: function parameters + Can be none: `()` + Example: `main` + Can be multiple: `(arg1: Type1, arg2: Type 2, arg3: Type3)` + `: Double`: return data type + The type of the output data of the function ### Body + Contain the reusable code. + Use `return` statement to end the function and pass the result to "caller". + Special case: if the return type is omitted in header or the return type is `Unit` + `return` does not need a value to pass to caller. + When the code runs to the end of the body, the function automatically returns. ### Scope + The function: + Defined by visibility modifier + The variables and values in function body + Cannot be seen by other function (function bodies) + File-level variables and values + Defined outside of function + Can be seen by all function bodies in the file ## Call a Function + `functionName(arg1,...,argN)` + Examples: + `scoreToGPA(56)` + After the execution of `scoreToGPA`, the return value will "replace" `scoreToGPA(56)` with `1.0` in the statement. + `print("Please input an integer: ")` + Often used as a statement + Member function: + Function associated with certain type of data + Example: `Scanner.next`, `Scanner.nextInt`, `Scanner.nextDouble` ## Practice: Write Functions 1. Modify the function `isLeapYear` ```kotlin import java.util.Scanner fun main() { val scanner = Scanner(System.`in`) val year = 2020 // scanner.nextInt() // 2020 is a leap year! println(if (isLeapYear(year)) "Yes" else "No") } private fun isLeapYear(year: Int): Boolean { return false } ``` 2. Modify the function `greet` to print greeing messages with name. ```kotlin import java.util.Scanner fun main() { val scanner = Scanner(System.`in`) val name = "MZ" // scanner.next() // Should print "MZ san, konnichi wa." greet(name) } private fun greet(name: String): Unit { println("Mina san, konnichi wa.") } ``` 3. Modify the function `sumUp` to compute the sum of elements in an `IntProgression` ```kotlin import java.util.Scanner fun main() { val scanner = Scanner(System.`in`) val first = 1 // scanner.nextInt() val last = 7 // scanner.nextInt() val gap = 3 // scanner.nextInt() // Should print 12 println(sumUp(first..last step gap)) } private fun sumUp(progression: IntProgression): Int { return 0 } ``` 4. Modify the function to compute the average of two `Double` values. ```kotlin import java.util.Scanner fun main() { val scanner = Scanner(System.`in`) val x = 113.0 // scanner.nextDouble() val y = 114.0 // scanner.nextDouble() // Should print 113.5 println(average(x, y)) } private fun average(x: Double, y: Double): Double { return 0.0 } ``` 5. Complete the function to compute the median of three `Int` values. ```kotlin import java.util.Scanner fun main() { val scanner = Scanner(System.`in`) val x = 113 // scanner.nextInt() val y = 222 // scanner.nextInt() val z = 87 // scanner.nextInt() // Should print 113 println(medianOf(x, y, z)) } private fun medianOf(a: Int, b: Int, c: Int): Int { return 0 } ``` ## Default Argument and Named Argument ```kotlin private fun bless(country: String = "Taiwan", god: String = "Mazu") { println("$god bless $country") } fun main() { // call bless with default arguments // should print "Mazu bless Taiwan" bless() // call bless with positional arguments // should print "God bless America" bless("America", "God") // call bless with named arguments // should print "God bless America" bless(god = "God", country = "America") // country: default argument // god: named argument // should print "God bless Taiwan" bless(god = "God") // country: positional argument // god: default argument // should print "Mazu bless America" bless("America") } ``` ## Single-Expression Functions If the function body is simply ```kotlin { return expression } ``` then we can replace it with ```kotlin = expression ``` + Example 1 ```kotlin fun squareOf(n: Int): Int { return n * n } ``` is equivalent to ```kotlin fun squareOf(n: Int) = n * n // The return type can be derived. ``` + Example 2 ```kotlin fun difference(x: Int, y: Int): Int { return if (x > y) x - y else y - x } ``` is equivalent to ```kotlin fun difference(x: Int, y: Int) = if (x > y) x - y else y - x ``` + Special case ```kotlin fun main() { println("hello") } ``` is equivalent to ```kotlin fun main() = println("hello") ``` ## Reserved Words and Backticks + Reserved words in Kotlin + `val`, `var`, `fun`, `if`, `else`, `when`, `while`, `for`, ... + They are invalid identifiers + Use backticks ``` ` ``` to escape. + Example: ```System.`in` ``` ## Anonymous Functions + Function defined with a name: named function + Function defined without a name: anonymous function + lambda + Define anonymous functions like data + With proper function type + With implicit return + With `it` or proper arguments + Examples ```kotlin import java.util.Scanner fun main() { val scoreToGPA: (Int) -> Double = { score -> when (score) { in 90..100 -> 4.3 in 85..89 -> 4.0 in 80..84 -> 3.7 in 77..79 -> 3.3 in 73..76 -> 3.0 in 70..72 -> 2.7 in 67..69 -> 2.3 in 63..66 -> 2.0 in 60..62 -> 1.7 in 50..59 -> 1.0 else -> 0.0 } } val scanner = Scanner(System.`in`) print("What is your score? ") val yourScore = scanner.nextInt() val yourGPA = scoreToGPA(yourScore) print("What is his score? ") val hisScore = scanner.nextInt() val hisGPA = scoreToGPA(hisScore) when { yourGPA > hisGPA -> println("Your GPA is better than his.") yourGPA < hisGPA -> println("His GPA is better than yours.") else -> println("Your GPA is equal to his.") } } ``` ### Function Type + Like data, functions have types. + `fun funName(arg1: Type1,...,argN: TypeN): RetType` + `funName` has type `(Type1,...,TypeN) -> RetType` + Unit Function: `(Type1, ..., TypeN) -> Unit` + Example: + `main` has type `() -> Unit` + `scoreToGPA` has type `(Int) -> Double` ### Implicit Returns + The last expression in the function body of anonymous function is the return value + You may not use `return` in anonymous function. ### Argument Name + When we define an anonymous function, we do not define the names of the arguments with their types. + Names are defined in the function body. ```kotlin fun difference(x: Int, y: Int): Int { return if (x > y) x - y else y - x } ``` is equivalent to ```kotlin val difference: (Int, Int) -> Int = { x, y -> if (x > y) x - y else y - x } ``` ### `it` Keyword When the function has exactly one argument, then we can use `it` to simplify the code. ```kotlin import java.util.Scanner fun main() { val scoreToGPA: (Int) -> Double = { when (it) { in 90..100 -> 4.3 in 85..89 -> 4.0 in 80..84 -> 3.7 in 77..79 -> 3.3 in 73..76 -> 3.0 in 70..72 -> 2.7 in 67..69 -> 2.3 in 63..66 -> 2.0 in 60..62 -> 1.7 in 50..59 -> 1.0 else -> 0.0 } } val scanner = Scanner(System.`in`) print("What is your score? ") val yourScore = scanner.nextInt() val yourGPA = scoreToGPA(yourScore) print("What is his score? ") val hisScore = scanner.nextInt() val hisGPA = scoreToGPA(hisScore) when { yourGPA > hisGPA -> println("Your GPA is better than his.") yourGPA < hisGPA -> println("His GPA is better than yours.") else -> println("Your GPA is equal to his.") } } ``` ## Type Inference Support + Specify the types of the arguments, then Kotlin can derive the type of the anonymous function. ```kotlin val difference: (Int, Int) -> Int = { x, y -> if (x > y) x - y else y - x } ``` is equivalent to ```kotlin val difference = { x: Int, y: Int -> if (x > y) x - y else y - x } ``` ## Function Reference + Since we have function types in Kotlin, we can use function types as arguments and return types. + `String.count` is a function that accepts a function as an argument. See the [document](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/count.html). + Function Reference: use `::` operator to obtain the "reference" of a named function. ```kotlin fun isN(chr: Char): Boolean { return chr == 'N' || chr == 'n' } fun isC(chr: Char) = chr == 'C' || chr == 'c' fun main() { val univ = "National Yang Ming Chiao Tung University" val total = univ.count() println("Total characters: $total") val numN = univ.count(::isN) println("Number of N's: $numN") val numC = univ.count(::isC) println("Number of C's: $numC") // val isT = {chr: Char -> chr == 'T' || chr == 't'} val isT: (Char) -> Boolean = {it == 'T' || it == 't'} val numT = univ.count(isT) println("Number of T's: $numT") // val numU = univ.count({it == 'U' || it == 'u'}) // short hand example val numU = univ.count{it == 'U' || it == 'u'} println("Number of U's: $numU") } ``` + Short hand: + If the only argument is a function and we are going to pass an anonymous function, then we can ignore the brackets `(` and `)` to simplify the notation. + If the last argument is a function and we are going to pass an anonymous function, then we can omit the last argument and append the anonymous function.