Zero, preface,

Recently, Kotlin has had a good time comparing Java and JavaScript. Unfortunately, Java’s functions are too low. Kotlin completely makes up for the shortcomings of Java in terms of functions. Although java8 does support lambda expressions, it’s not as cool as kotlin’s. Today’s talk is only about functions and lambdas. I won’t teach you how to do functional programming.


First, start with Kotlin’s function

In Java, it doesn’t seem to talk much about functions, but methods. Methods are the behavior of objects. What is a function?

0. What is a function?

High school math defines the concept of a function like this:

Let A and B be non-empty sets of numbers, if, according to some definite correspondence f, for any number x in set A, there is A unique definite number f(x) corresponding to it in set B, then it is called"F: A and B"For A function from set A to B, remember: y = f (x), x ∈ a. among them, called the independent variable x, x value range is called function (domain) and the value of x y value of the corresponding called function value, the set of function values {f (x) | x ∈ a.} [range] called functionCopy the code

In mathematics, the composition of a function is two sets and a corresponding rule, and each independent variable can obtain a unique dependent variable under the mapping of the corresponding rule. I prefer to think of a function in mathematics as a set of all variations of an independent variable under the corresponding rules. This seems to be a different concept from a function in programming, but there are some ideological similarities:

If you think of an independent variable as an input state, under the corresponding law, each input corresponds to a unique output state. The functions in programming do the same thing: the input material data is processed logically to form a specific output, but with more varying dimensions (parameters)Copy the code

1. The form of the function in Kotlin

For the function that takes the face, it always has a unique y output for the input x

fun fx(x: Int): Int {
    val y = x + 2
    return y
}
Copy the code

“– You may be saying,” This is just adding a 2. SQRT (math.exp (x) -3 * math.acos (x)) -math.log (x) In my eyes, this is just a correspondence relation, its essence and its representation have nothing to do with it, even if it is written as val y = 1, its essence will not change: it is still the input x can always maintain a unique y output, this is abstract, too much attention to the representation will be superficial to the limit of vision.


2. The types of functions in Kotlin

A function in Kotlin is also a data type, with the type: (parameter type, parameter type)-> Return value type. Kotlin uses the :: function name to get a reference to a function that can exist as an object

Val line: (Double) -> Double line = ::fx line(8.0)//10.0 println(line)//fun fx(kotlin.double): kotlin.Double println(line is (Double) -> Double)//true| - in effect, the common view is to make the + 2, there is no great | - but from the macro view the function implements a y = x + 2 linear data converter, isn't it a bit on the tallCopy the code

3. The input parameters of the function

There is now a gX, which implements a y=e^x data converter.

fun gx(x: Double): Double {
    val y = Math.exp(x)
    return y
}
Copy the code

You might think: well, since functions can be objects, they can also be inputs and then you accidentally spell out this nice looking function, let’s put fx as the input toe y = e^(x+2), and that’s what we’re going to do.

fun gx(x: Double, f: (Double) -> Double): Double {
    val y = Math.exp(f(x))
    returnY} println (gx (0.0, : : fx)) / / 7.38905609893065Copy the code

4.Lambda

The input is a function. The function can be written as a Lambda expression, where the input type of gx is: (Double) -> Double {parameter name :Double -> return Double};}} {parameter name :Double -> return Double};}

| - use anonymous functions, without a Lambda gx (5.0, fun (x: Double) : Double {returnMath. Sin (x)}) | - use the existing function, need not Lambda gx (5.0, : : sin) | - using Lambda, standard -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- gx (5.0, {x: Double - > Math. Sin (x)}) / / | - Lambda features: 0.3833049951722714 as the last parameter can be set after -- -- -- -- -- -- -- -- -- -- -- -- -- -- gx (5.0) {x: Double - > Math. Sin (x)} / / | - 0.3833049951722714 can derive the variable type, Can be omitted variable type -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- gx (5.0) {x - > Math. Sin (x)} / / 0.3833049951722714 | - can use it instead of only one parameter, omitted variable -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- - Gx (5.0) {math.sin (it)}//0.3833049951722714Copy the code

All right, the introduction of Lambda is done, maybe you’re a little confused, that’s ok, keep going


2. Look at lambda expressions from map function

All languages have operators such as map. Take Kotlin for example
Val ints = IntArray(10) {it}// initialize 0 1 2 3 4 5 6 7 8 9 ints.map {it * it}.foreach {print("$it "); }//0 1 49 16 25 36 49 64 81Copy the code

2. Source analysis of Array map function
---->[_Arrays.kt#map]-----------------------
public inline fun <R> IntArray.map(transform: (Int) -> R): List<R> {
    returnMapTo (ArrayList < R > (size), the transform)} | - map function into the parameter is a function of (Int) - > R type, the return value is a List of < R > | - it calls the mapTo method -- -- -- - > [_Arrays. Kt#mapTo]-----------------------
public inline fun <R, C : MutableCollection<in R>> IntArray.mapTo(destination: C, transform: (Int) -> R): C {
    for (item in this)
        destination.add(transform(item))
    returnDestination} | - this method head is a bit long, take a closer look at: Method into the destination, type C, where C is MutableCollection type | - from the above incoming ArrayList < R > (size), is an empty list of size size, The second parameter is still just a function of the transform | -- let this all elements through the transform method, and then join the null columns list, then will return to go out to destination | - such a look at the map method also have no imagination of so magical, also can see the map will not pollution of the original arrayCopy the code

3. Map in Java stream

One of the most common interfaces for lambda expressions in Java is a method, which is common in streams

List<Integer> ints = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> list = ints.stream()
        .map((e) -> {
            returne * e; }) .collect(Collectors.toList()); | - shorthand List < Integer > List = ints. Stream (). The map (e - > e * e) collect (Collectors. ToList ()); ---->[What are lambda expressions in Java?] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- source: Stream#map------------<R> Stream<R> map(Function<? super T, ? extends R> mapper); | - we can see in and out of the parameter is a Function type, there are two generic T and R | - what is the Function object ghost? @FunctionalInterface public interface Function<T, R> { R apply(T t); default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
    
    static <T> Function<T, T> identity() {
        returnt -> t; }} | - Functions provides an interface, there are two generic: T and R, the apply function and T type parameters, return a R type value * @ param < T > thetype of the input to the functionType of input * @param <R> thetype of the result of the functionOutput type | - including the structure of the compose and andThen two default interface, it seems that compose can cut hu, go first wave Function of before | - andThen instead, go first apply, And then go after the apply | - for example, I have a piece of candy, compose is eaten spit it out and give me to eat, I ate, andThen is spit it out to give her to eat | - variable extraction, Function<Integer, Integer> fn = e -> e * e; fn.apply(8); //64 fn.compose((Integer e) -> { System.out.println();return e * 2;
        }).apply(8)//256 = (8*2)^2

fn.andThen((Integer e) -> {
            System.out.println();
            returne * 2; }).apply(8)); / / 128 = 8 * 8 * 2Copy the code

Lambda expressions in JavaScript

Similarly, you don’t change the array

let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let result = arr.map(e => {
    returne * e; }); console.log(arr); //[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] console.log(result); / / [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] | - shorthand:let result = arr.map(e => e * e);
Copy the code

Lambda expressions in Python

How many lines of Python lambda expressions… Still hope give directions, on the net is a line…

arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
result = map(lambda e: {e * e}, arr)
print(arr)# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(list(result))  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]| - shorthand result = map (lambda e: e * e, arr)Copy the code

6. The lambda Dart
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var result = arr.map((e) => (e * e));
print(arr); //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]print(result); / / (0, 1, 4, 9, 16, 25, 36, 49, 64, 81) | -- shorthand var result = arr. The map (e) (e) = > e *;Copy the code

As you can see, each language has a different representation of lambda expressions. Here are the full unabbreviated and abbreviated lambda expressions for each language

| - Kotlin val fn = {e: Int - > {e * e}} shorthand: val fn = {e: Int -> e * e } |-- Java Function<Integer, Integer> fn = (Integer e) -> {returne * e; }; Short form: Function<Integer, Integer> fn = e -> e * e; |-- JavaScriptlet fn = (e) => {
    returne * e }; Abbreviations:letfn = (e) => e * e; | - Python fn = lambda e: {} e * e shorthand: fn = lambda e: e * e | - Dart var fn = (e) = > (e * e); Var fn = (e) => e * e;Copy the code

3. Lambda expressions from the perspective of addition

Lambda expressions are just a special way of writing functions, which are functions themselves, and can be assigned to variables and called

1. The Kotlin version
| - fun add additive function (x: Int, y: Int) : Int {returnX + y} | - into a lambda expressions val add = {x: Int, y: Int - > {x + y}} shorthand: val add = {x: Int, y: Int - > x + y} | - lambda expressions can be as a common function to call the add (3, 5) / / 8 | - see pass in a function such as the add method, it before and the x, y for processing fun add (x: Int, y: Int, fn: (Int) -> Int): Int {returnFn (x) + fn (y)} | - so you can calculate the x, y is the sum of the squares of the (3) ^ 2 + 4 ^ 2 = 25 val result = add (3, 4) {e} - > e * e x | - this can be computed, the absolute value of y and: | 3 | | | + 4 = 7 val result = add (3, 4) {e - > Math. Abs (e)} | - advantage is self-evident, can customize the development use, should you need | - if you think that trouble, of course, as with the, Fun add(x: Int, y: Int, fn: (Int) -> Int = {e -> e}): Int {return fn(x) + fn(y)
}
val result = add(-3, 4) //1
Copy the code

2. Java version

Java is not as free-flowing as modern languages. As we can see from the Function above, it is the interface that enables Java to support lambda expressions. Since Java has a Function interface, we can also customize it

-- -- -- - > [definition method interface] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the public interface AddFun < T, R > {R apply (T, x, y, T); } | use -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- AddFun < Integer, Integer > add = (x, y) - > x + y; Integer result = add.apply(4, 5); | - how to expand into the above that the custom addition? | - that is, add a (Function) into the ginseng, can pass in lambda expressions public interface AddFun < T, R > apply {R (T, x, T y, Function <? super T, ? extends R> rule); } AddFun<Integer, Integer> add = (x, y, rule) -> rule.apply(x) + rule.apply(y); Integer result = add.apply(3, 4, e -> e * e); //25 Integer result = add.apply(-3, 4, e -> Math.abs(e)); //7 Integer result = add.apply(-3, 4, Math::abs); / / 7 shorthandCopy the code

3. The JavaScript version
| - addition function as lambda expressionsletla = (x, y) => x + y; console.log(la(3, 4)); / / 7 | - adding + lambda expressions into the refsfunction add(x, y, fn = e => e) {             
    return fn(x) + fn(y);
}

let a = add(-3, 4, e => e * e);
letb = add(-3, 4, e => Math.abs(e)); console.log(a); //25 console.log(b); / / 7 | - together can also be writtenletla = (x, y, fn) => fn(x) + fn(y); la(-3, 4,e => e * e); / / 25Copy the code

4. Python and Dart

It’s all the same, so let’s cut to the chase

|-- Python
add = lambda x, y: x + y
addex = lambda x, y, fn: fn(x) + fn(y)
a = add(3, 4)
b = addex(-3, -4, lambda e: e * e)
print(a)# 7
print(b)# 25

|-- Dart
var add = (x, y)=> x + y;
var addex = (x, y, fn) => fn(x) + fn(y);
var a = add(3, 4);
var b = addex(-3, -4, (e)=> e * e);
print(a); / / 7print(b); / / 25Copy the code

Four, let’s finish with some higher-order functions

Java stream manipulates cluster elements, Kotlin manipulates cluster elements, passes functions, it’s easy to use lambda expressions and JavaScript,Python, and Dart are all involved in clustering in some way or another The reduce, etc.

1. Java stream
|-- forInts.stream ().foreach (e->{system.out.println (e); }); | - allMatch operation: according to the condition control traversal, to see if all accords with a condition, as long as there is a unqualified, interrupt traversal and returnfalse
List<Integer> ints = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
Stream<Integer> stream = ints.stream();
boolean b = stream.allMatch(e -> {
    System.out.println(e);
    return e < 5; //0 1 2 3 4 5
});
System.out.println(b);//falseReturn whether all requirements | - anyMatch operation: according to the condition control traversal, see whether to have qualified, as long as there is a qualified, interrupt traversal and returntrue
boolean has = ints.stream().anyMatch(e -> {
    System.out.println(e);
    return e > 5; //0 1 2 3 4 5 6
});
System.out.println(has);//true| - noneMatch operation: according to the condition control traversal, see whether to have qualified, as long as there is a qualified, interrupt traversal and returnfalseboolean hasNot = ints.stream().noneMatch(e -> { System.out.println(e); //0 1 2 3 4 5 6returne >5 ; }); System.out.println(hasNot); //false| - filter operation: need to filter out elements, return the remains of the stream, so can use ints. Continuous stream () filter (e - > e % 2 = = 0). The forEach (System. Out: : println); / / 0 2 4 6 8 | - map operations: all of the elements can be in accordance with the rules of the change, return the remains of the stream | - collect operation: List<Integer> List = ints.stream().map(e -> e * e).collect(Collectors. ToList ()); System.out.println(list); | - flatMap: operation will flatter hierarchy structure. Such as the three thieves, everyone stole a few things set (elements) | - and three men were caught by the police, three people at a time to steal things on the table one by one, ok, FlatMap List<Integer> int0to4 = Arrays.aslist (0, 1, 2); List<Integer> int3o7 = Arrays.asList(3, 4); List<Integer> int4to8 = Arrays.asList(4, 5); Stream.of(int0to4, int3o7, int4to8).flatMap(list -> list.stream()) .forEach(System.out::println); / / 0 1 2 3 4 5 | - 4limitOf n elements, to return before operation: capture is still stream | - skip operation: ForEach (system.out ::println); forEach(system.out ::println); forEach(system.out ::println); / / 2, 3, 4, 5 | - findFirst: access to the first element of flow int STR = ints. Stream () filter (x - > x < - 3) / / filtering flow. FindFirst () / / the first orElse (10000); // The default value is system.out.println (STR); / / 4 | - mapToInt: int flow formation, advantage with the additional apis IntSummaryStatistics stats = ints. Stream () mapToInt (x (x) - >). SummaryStatistics (); System.out.println("max : "+ stats.getMax()); //9 System.out.println("min : " + stats.getMin()); //0 System.out.println("sum : "+ stats.getSum()); //45 System.out.println("ave : "+ stats.getAverage()); / / System. 4.5 out.println ("count : "+ stats.getCount()); / / | 10 - Max and min operation, both on the contrary, passing a comparator, returns an Optional object int Max = ints. Stream (). The Max ((o1, o2) - > o1, o2). The get (); int min = ints.stream().min((o1, o2) -> o1 - o2).get(); System.out.println(max+"--"+min); / / 9-0 | - reduce operation: Integer reduce = ints. Stream (). The reduce (0, (the result, the value) - > {System. Out. Println (result +"-" + value);
    returnresult + value; }); System.out.println(reduce); Feel Reduce is super fun: It feels like a snake, eating one by one, but before eating the next, the effect of eating the previous one is still in which the first parameter is the offset, which can be regarded as the initial situation of the snake, on this basis, every time I go through it, Eat a 0-4-0 0 0-1-1-1-4-2-2-3 of 3 7-3 the initial value of 0 6-4 initial value 4 10-4-5 to 14-5-15 June 19th - June 21 - July 25-7 28-- 8 32-- 8 36-- 9 40-- 9 45 49Copy the code

2.Kotlin
|-- forEach operation: iterates over the element ints.foreach {print("$it ") / / 0 1 2 3 4 5 6 7 8 9} | -- all operation: according to the condition control traversal, to see if all accords with a condition, as long as there is a unqualified, interrupt traversal and returnfalse
val b = ints.all {
    println(it);
    it < 5; //0 1 2 3 4 5
}
println(b) //false| - any operation: according to the condition control traversal, see whether to have qualified, as long as there is a qualified, interrupt traversal and returntrueval any = ints.any { println(it); it > 5; ////0 1 2 3 4 5 6 } println(any)//true| - noneMatch operation: according to the condition control traversal, see whether to have qualified, as long as there is a qualified, interrupt traversal and returnfalse
val any = ints.none() { println(it); it > 5; //0 1 2 3 4 5 6 } println(any)//false| - filter operation: filter out elements that need to be, no damage to the original array ints. Filter {it % 2 = = 0}. The forEach {print("$it "); } / / 0 2 4 6 8 | - map operations: all of the elements can be in accordance with the rules of the change, return is still a stream of ints. Map. {it} it * forEach {print("$it "); } / / 0 1 4 September 16 to 25 36 64 | 81-49 dropWhile operation: Know that meet the conditions before deleting elements of val list = ints. DropWhile {it < 6} println (list) / / [6, 7, 8, 9] | - reduce operation:  val reduce = ints.reduce { result: Int, value: Int -> println("$result --- $value")
    result + value
}
println(reduce)
Copy the code

Finally, a lambda expression represents an interface object in Java, and a function in modern languages

var la={x: Int ,y:Int-> x +y}
println(la is (Int, Int) -> Int)//true
println(::add is (Int, Int) ->Int)//true

fun add(x: Int, y: Int): Int {
    return x + y
}

Copy the code

About each language understanding depth is different, if there is a mistake, welcome to criticize and correct.


Postscript: Jie wen standard

1. Growth record and Errata of this paper
Program source code The date of The appendix
V0.1, The 2018-3-6 There is no

Release name: look from the five language functions and lambda expressions express text links: https://juejin.cn/post/6844903788612943885

2. More about me
Pen name QQ WeChat
Zhang Feng Jie te Li 1981462002 zdl1994328

My github:https://github.com/toly1994328 Jane books: https://www.jianshu.com/u/e4e52c116681 my Jane books: https://www.jianshu.com/u/e4e52c116681 website: http://www.toly1994.com

3. The statement

1—- this article is originally written by Zhang Feng Jetelie, please note 2—- all programming enthusiasts are welcome to communicate with each other 3—- personal ability is limited, if there is any error, you are welcome to criticize and point out, will be modest to correct 4—- See here, I thank you here for your love and support