Kotlin: define higher order functions

If a function receives another function as a parameter , or the type of the return value is another function, then the function is called a higher-order function.

This definition may not be easy to understand. How can a function receive another function as a parameter? This involves another concept: function type.
We know that there are integer, boolean and other field types in programming languages, and Kotlin Added the concept of a function type. If we add this function type to the parameter declaration or return value declaration of a function, then this is a higher-order function.

So, how to define a function type? The
basic rules are as follows:

(String,Int)  ->Unit

-> The left part is used to declare what parameters the function accepts. Multiple parameters are separated by commas. If no parameters are received, just write a pair of empty parentheses.
And the right part is used to declare the What is the return value of the function? If there is no return value, use Unit, which is roughly equivalent to void in Java .

Now add the above function type to the parameter declaration or return value declaration of a function, then this function is a higher-order function. As shown below:

fun example(func: (String,Int) ->Unit){
    
    
    func("hello",123)
}

As you can see, the example() function here receives a function type parameter, so the example() function is a higher-order function. To call a function type parameter, its syntax is similar to calling an ordinary function, only Add a pair of parentheses after the parameter name, and pass in the necessary parameters in the parentheses.

We first define higher-order functions as follows:

fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int {
    
    
   val result = operation(num1, num2)
   return result
 }

This is a very simple high-order function. It may not have much practical meaning, but it is a good learning example. The first two parameters of the num1AndNum2() function are not well explained. The third parameter is one receiving two Integer parameters and the return value are also integral function type parameters. In the num1AndNum2() function, we did not perform any specific operations, but passed the num1 and num2 parameters to the third function type parameter and obtained it The return value of, the final return value will be returned.

How to call it? We add the following code:

fun plus(num1: Int, num2: Int): Int {
    
    
        return num1 + num2
}

fun minus(num1: Int, num2: Int): Int {
    
    
   return num1 - num2
}

Two functions are defined here, and the parameter declarations and return values ​​of these two functions are exactly the same as the function types in the num1AndNum2() function. Among them, the plus() function adds the two parameters and returns, minus( ) Function subtracts and returns two parameters, corresponding to two different operations.

With the above function, we can call the num1AndNum2() function, the code is as follows:

 val num1 = 100
 val num2 = 80
 val result = num1AndNum2(num1, num2, ::plus)
 val result2 = num1AndNum2(num1, num2, ::minus)

Note that this call num1AndNum2 () function mode, the third parameter ::plusand ::minusthis formulation. It is written in a functional way of reference, shows a PLUS () and minus () function is passed as a parameter to num1AndNum2 () function. Since the num1AndNum2() function uses the passed function parameters to determine the specific operation logic, the plus() and minus() functions are actually used to operate on the two numbers.

Although using this kind of function reference writing can work normally, if every time you call any higher-order function, you must first define a function that matches its function type parameters. Is this a bit complicated?

That's right, so Kotlin also supports many other ways to call higher-order functions, such as lambda expressions, anonymous functions, member references, etc.

Among them, Lambda expression is the most common and common way of calling higher-order functions, and it is also the content we will focus on next.

val num1 = 100
val num2 = 80
val result = num1AndNum2(num1, num2) {
    
     n1, n2 -> n1 + n2 }
val result2 = num1AndNum2(num1, num2) {
    
     n1, n2 -> n1 - n2 }

As can be seen from the above, Lambda expressions can also completely express the parameter declaration and return value declaration of a function (the last line of code in the Lambda expression will automatically be the return value), but the writing method is more concise.

Let's continue to explore higher-order functions.
After learning the apply function, it can be used to provide a specified context for the Lambda expression. When multiple methods of the same object need to be called consecutively, the apply function can make the code change To be more concise, such as StringBuilder is a typical example.
Next, we will use higher-order functions to simulate a similar function.

fun StringBuilder.build(block: StringBuilder.() -> Unit): StringBuilder {
    
    
        block()
        return this
    }

Here we define a build extension function for the StringBuilder class, this extension function receives a function type parameter, and the return value type is also StringBuilder.

Note that the declaration of the function type parameters is different from the syntax we learned earlier: it adds a StringBuilder.syntax structure in front of the function type .

What does this mean? In fact, this is the complete grammatical rules for defining higher-order functions. Adding the function type in front of ClassName.it indicates which class the function type is defined in.

So what are the advantages of defining the function type in the StringBuilder class? The advantage is that the Lambda expression passed in when we call the build function will automatically have the context of the StringBuilder, and this is also the implementation of the apply function.

Now we can use the build function we created to simplify the way StringBuilder builds strings. The code is as follows:

    fun main() {
    
    
    
        val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
        val result = StringBuilder().build {
    
    
            append("Start eating fruits.\n")
            for (fruit in list) {
    
    
                append(fruit).append("\n")
            }
            append("Ate all fruits.")
        }
        
        println(result.toString())
    }

It can be seen that the usage of the build function is basically the same as the apply function, but the build function we write can only work on the StringBuilder class at present, and the apply function can work on all classes. If you want to implement the apply function This function requires the help of Kotlin's generics, and the relevant content will be studied in the next part.

Guess you like

Origin blog.csdn.net/gaolh89/article/details/107774517