[deep kotlin] - lambda expressions and higher-order functions

Functional programming

The function does not depend on the existence of any class (from the source code level, from the bytecode level, it still depends on the existence of the class). Typical examples are top-level functions.

lambda expression

lambda expressions are the same as java, which consists of two parts separated by the "arrow" symbol (- >). On the left of the arrow is the parameter list wrapped in parentheses (if there is only one parameter, the parentheses can be omitted), and on the right of the arrow is the return value type:

// Defines the type of lambda expression
(a: Int, b: Int) -> Int
// Defining lambda expression instances
{ a,b -> a+b } // Curly braces are wrapped and cannot be omitted
// Infer the type of lambda by lambda instance
val add = { a:Int, b:Int -> a+b }
// No parameter, no return value (or Unit)
val action = { println("hello") }
// Allow function to be empty
val action: ((Int,Int) -> Int)? = null

lambda expressions as arguments

fun substract(a:Int, b: Int) = println(a-b)
fun test(a: Int = 1, b: Int = 2, compute: (x:Int, y:Int)->Unit) { 
	compute(a,b)
}
... ...
test(2,3,::substract) // 1
test(2,3, { a,b -> println(a-b)}) // 2
test(2,3){ a,b -> println(a-b)} // 3
test { a,b -> println(a-b) } // 4

The compute parameter is a lambda expression that receives two parameters and returns unit (that is, void, no return value).

  1. Here, a method reference (java8) is passed to the compute parameter. The method reference is in the form of class name:: method name. Here, the subject is the top-level object and there is no class name, so the class name is omitted.
  2. Pass lambda expressions directly. Note that the A and b parameter lists do not need to be enclosed in parentheses.
  3. Trailing closure.
  4. Use the default parameter writing method. Note that the parentheses including the method parameters can be omitted. At the same time, the trailing closure does not need to specify the parameter name.

Arguments to lambda expressions

fun test(x: Int, action:()-> Unit){

}

fun test2(x: Int, action:(Int)->Unit){

}
fun main(){
	test(5){ println("aaa")} // 1
  test2(5){ println("bbb")} // 2
}
  1. The action parameter of test is a lambda without parameters, so there is no need to declare any lambda parameters when calling.
  2. The action parameter of Test2 is a lambda with parameters, but you can also call without declaring any lambda parameters. This is a language feature of kotlin. If the lambda expression of a method has only one parameter, it can be ignored when calling. If you want to use this parameter, you can reference it with an implicit parameter (it keyword).
fun test3(){}
fun test4(x:Int){}
fun test5(x:Int, action:(Int,Int)->Unit){
	action(1,2)
}
fun test6(x:Int, y:Int){ println(x+y) }
fun main(){
  test(5, ::test3)
  test2(5, ::test4)
  test5(5, ::test6) // 1
  
  test5(5){ // 2
    println("a")
  }
}
  1. Pass the function reference of test 6 to the second parameter of test5. In this way, the only sentence of test5: action(1,2) becomes test6(1,2), and the result prints out: 3.
  2. Compilation error. Because the action of test5 requires two parameters, the parameters of lambda expression cannot be omitted and must be declared.

Higher order function

A function that receives a function as a parameter or a function as a return value is called a high-order function. If the last parameter of a function is a function, it can be in the form of lambda trailing closure. In Kotlin, lambda expressions are functions.

fun calculate(a:Int, b:Int, operation: (Int, Int)->Int): Int{ // 1
  return operation(a,b) // 2
}
... ...
println(calculate(2,3){a,b -> a+b})
  1. The last argument to the calculate function is a function (that is, a lambda expression), so it is a high-order function.
  2. Call the lambda expression and return the call result.

Take another example:

fun String.filter(predicate: (Char,Int) -> Boolean): String { // 1
    val i = 0
    var result = ""

    for(c in this.toCharArray()){ // 2
        if(predicate(c, i)){ // 3
            result += c
        }
    }
    return result // 4
}

fun main(args: Array<String>) {
    var string = "1324q22ddd"

    string = string.filter(){
        ch, index ->  ch.isDigit() // 5
    }
    println(string) // Print: 132422
}
  1. Extend a filter function for String. This function is a high-order function with a lambda as the parameter. This lambda will accept two parameters: a character and the index of the character in the String, and return Boolean to indicate whether to accept the character.

  2. Traverses each character in the string.

  3. Call the lambda expression with this character and its index. If the expression returns true, the character will be included in the return result. Otherwise, the character will be filtered.

  4. After traversal, the filtered result (a new string) is returned.

  5. Call filter and use a lambda expression as a parameter. The lambda expression will judge according to the incoming character. If the character is a number, it returns true, otherwise it returns false. Note that the value of the last expression of the lambda expression will be used as the return value of the lambda expression by default, so there is no need to return. If you want to return, you need to return in a fully qualified way.

    return@filter ch.isDigit() // Use the @ sign to explicitly specify whether to return to the filter instead of the current function
    

Keywords: kotlin

Added by Moon-Man.net on Mon, 17 Jan 2022 16:24:37 +0200