What is a lambda

A Lambda expression, or Lambda for short, is an object that contains a block of code. Note that it is an object. We can assign it to a variable, like any other type of object, or we can pass it to a method that executes the code block it contains. That is, you can use lambda to pass specific behavior to a more general method.

Lambda of actual combat

The format of a lambda expression

So let’s see how do we write a lambda

{ x: Int -> x + 5 }
Copy the code

This is the simplest lambda expression that increments the value of the Int argument by 5. Lambda starts and ends with curly braces. All lambdas are defined in curly braces and cannot be omitted.

In curly braces, a single Int argument x is defined using x:Int. Lambda can have a single argument, multiple arguments, or no arguments.

The argument is followed by ->, which separates the argument from the body, followed by the body, x+5, which can have multiple lines, the last of which will be the return value of the lambda expression.

Let’s write a lambda with multiple arguments

{a:Int,b:Int -> a+b}
Copy the code

Assign a lambda to a variable

We can assign lambda expressions to variables just like any other object, following the example above:

var addFive = { x: Int -> x + 5 }
Copy the code

We define a variable called addFive, which assigns code blocks to it when lambda is used, rather than the result of the code. To execute the code in lambda, we need to explicitly call it.

How do you make that call? We can use the invoke function to call a lambda and pass in the value of the argument,

var s = addFive.invoke(5)
println(s)
Copy the code

The output is 10.

You can also call it directly

println(addFive(5))
Copy the code

Also print 10.

Of course, either way, the resulting bytecode is called through the Invoke method.

The expression type of Lambda

Same as any other type (Int, Double, Float…) Lambda is also a type, except that it does not specify the class name for the implementation of lambda. Instead, it specifies the lambda parameter and return value types. The format of the type is as follows

(parameters) -> return_typeCopy the code

If your lambda takes a separate Int argument and returns a string

var l = {x:Int -> "$x"}
Copy the code

So, this lambda is of type

(Int) -> StringCopy the code

If you assign a lambda to a variable, the compiler uses the lambda to infer the type of the variable. Of course we can define the type of the variable explicitly, so let’s define the type of the variable explicitly

Var v: (Int)->String = {x:Int -> "$x"}Copy the code

Automatic resolution of lambda parameter types

When displaying a declared variable’s type, we can omit any type declarations that the compiler can infer from the lambda, as in var v: (Int) – > String = {x: Int – > “$x”}, among them, the compiler has been learned from the v type definition, any assignment to that variable lambda must have an Int parameter, this means that it can be omitted in lambda parameter definition Int type of statement, Since the compiler can infer its type, we can abbreviate it as:

var v : (Int)->String = {x -> "$x"}
Copy the code

Otherwise, if a lambda has a separate argument and the compiler is able to infer its type, it can omit that argument and replace it with the IT keyword in the body of the lambda.

It is the same statement as above, where there is only one parameter x of type Int, and the compiler can infer its type, so we can simplify the statement to

var v : (Int)->String = {"$it"}
Copy the code

Note here: You should only use the IT syntax if the compiler can infer the parameter type. If the compiler cannot infer, it cannot be used. Here is an example of an error:

var v  = {"$it"}
Copy the code

Take lambda as an argument to the function

In addition to assigning lambdas to variables, one or more lambdas can be used as arguments to functions, allowing us to pass specific behavior into other functions.

Let’s write a function that takes a lambda argument.

Fun convert(x:Double, converter: Double)->Double): Double {}Copy the code

This passes lambda as an argument to another function.

Now that we’ve defined the function, how do we make lambda arguments work in the new function? Now let’s define some functionality for the convert function to perform a data calculation. After passing in the argument x, we evaluate it through lambda and return the result.

The complete convert function logic is as follows:

fun convert(x:Double,converter:(Double)->Double): Double {// Call the converter lambda and assign its return value to result val result = Converter (x) // Return result}Copy the code

Through the above analysis, we can define the logic of the lambda parameter by ourselves, passing in the function body, we can realize the corresponding requirements.

Print (convert(10.0, {x:Double-> x+10}))Copy the code

Above, we passed in a simple lambda expression that adds 10 to the logic. The final input is 20.0.

There are a few points to highlight here: if lambda is the last argument to a function, as we defined convert above, it is possible to move the lambda argument outside the parentheses of the function call.

Print (convert (10.0) {x: Double - > x + 10})Copy the code

If the function has only one argument and the argument is lambda, the entire parentheses can be omitted when the function is called. Let’s modify the convert function above

Fun convert(converter: Double)->Double): Double {return Converter (10.0)}Copy the code

It now has a single argument of type lambda. Our normal call is:

Print (convert({x:Double -> x+10})) print(convert() {x:Double -> x+10})Copy the code

So this is just a shorthand for theta

print(convert {x:Double -> x+10} )
Copy the code

Get rid of the parentheses.

The function returns a lambda

In addition to taking a lambda as an argument, functions can also specify that they return a lambda by its type. As follows: if the x passed in is equal to 0, then the lambda multiplied by 10 is returned, otherwise the lambda multiplied by 20 is returned.

fun getLambda(x:Int):(Int)->Int {
    if(x == 0){
        return {m:Int -> m * 10}
    }else{
        return {it * 20}
    }
}
Copy the code

Now let’s call this method.

val m = getLambda(0)
print(m(10))
val n = getLambda(1)
print(n(10))
Copy the code

The printed results are 100 and 200.

Take lambda as the argument and return value of the function

Now let’s create a function that takes two lambda arguments, merges them, and returns a new lambda.

fun combine(lambda1:(Double)->Double, lambda2:(Double)->Double) : (Double)->Double{
    return {x:Double -> lambda2(lambda1(x))}
}
Copy the code

Let’s examine this function. First, the function takes two arguments lambda1, both of type lambda, and both return a Double.

The return value of the function is also a lambda, but the lambda1 and lambda2 lambdas are combined to do the final calculation.

So let’s call this function.

//✖️2 operator val l1 = {x:Double -> x*2} // operator val l2 = {x:Double -> x/4} // get a new lambda val combine = Combine (L1, L2) // Print the result of new lambda calls println(Combine (10.0))Copy the code

The final print result is 5.0. Now we give 10 to L1 for ✖️2, and then to L2 for ➗4 to get 5.0.

Make lambda more readable – typeAlias

The use of lambda function types makes our code more complicated and less readable. For example, in the above Combine function, lambda is nested and many references of function types will appear. In this case, we can replace function types with type aliases to make the code more readable.

Type aliases allow you to take an alternative name for an existing type, which can then be used in your code. The keyword TypeAlias is used to define aliases. To simplify our code, let’s modify the above Combine function.

First we define the type alias:

typealias balabala = (Double)->Double
Copy the code

Replace with a type alias:

fun combine(lambda1:balabala, lambda2:balabala) : balabala{
    return {x:Double -> lambda2(lambda1(x))}
}
Copy the code

When the compiler sees Balabala, it knows that this is a (Double)->Double placeholder.

conclusion

Above we introduced some knowledge about lambda, lambda is very widely used, it can be assigned to variables, can be a type, can be used as a function parameter and return value, and so on.

Lambda is an important part of functional programming, where non-functional programming reads input data and produces output data, while functional programming can take functions as inputs and produce functions as outputs. Understanding lambda will be more helpful in understanding functional programming.