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.