Deferred calculation (lazy evaluation) is the calculation of the value of an expression backward until the expression is actually used. Before discussing Lazy-evaluation, a special language attribute "compute Time" (strict-ness) in functional programming is introduced. Strict-ness refers to the point-in-time mode in which a system calculates a value for an expression: an instant calculation (strict), or a deferred calculation (non-strict or lazy). Non-strict or lazy means that it is counted when an expression is used. Use a simple and intuitive example to illustrate:
1 def lazyfun (x:int): Int = {2 println ("Inside function")3 x + 14 c10/>} //> Lazyfun: (x:int) Int5 lazyfun (3/0) // > Java.lang.ArithmeticException:/by Zero
Obviously, when we pass 3/0 as a parameter to Lazyfun, the system calculates the value of the parameter before entering the function, the calculation has an exception, and the result does not enter the function execution println directly exits. Now let's change the Lazyfun parameter declaration to: x: = = Int:
1def lazyfun (x: = = int): int = {2println ("Inside function")3X + 14}//> Lazyfun: (x: = = int) int5Lazyfun (3/0)//> Inside function6 //| java.lang.ArithmeticException:/by Zero7 //| At ch5.stream$ $anonfun $main$1$ $anonfun $1.apply$mci$sp (ch5.stream.scala:18 //| 0)
In this example, we once again passed a exception to Lazyfun. This time the system entered the function, we see println ("Inside function") or run. This means that the system does not ignore incoming parameters until the value of X is computed when the expression x + 1 uses this parameter x. We see that the type of parameter x is = = Int, which means that the x parameter is non-strict. The non-strict parameter is recalculated each time it is used. This is explained by the internal implementation mechanism: this is because the compiler (compiler) encounters the Non-strict parameter and puts a pointer in the call stack instead of the usual value of the parameter. So every time I use the non-strict parameter, I recalculate it. We can confirm from the following examples:
1 Def pair (x: = = int):(int, int) = (x, x) //> Pair: (x: = = int) (int, int)2 pair ( {println ("Hello ..."); 5}) //> Hello ... 3 // | hello ... 4 // | res1: (int, int) = (5,5)
In the above example, we pass the pair function to a code with the result of the Int class 5 as the x parameter. After returning the result (5,5) from two hello ... You can confirm that the parameters passed in were calculated two times.
In fact, Boolean expressions (Boolean expression) in many languages are non-strict, including && 。 x && y expressions if the X value is False, the system does not calculate the value of Y, but instead directly results in false. Same X | | Y is not calculated when the X value is true. Think of how much computing resources can be saved if y needs thousands of lines of code to calculate.
Take a look at one of the following if-then-else examples:
1def If2[a] (Cond:boolean, valtrue: = A, Valfalse: = a): a = {2 if(cond) {println ("Run valtrue ...")); Valtrue}3 Else{println ("Run Valfalse ...")); Valfalse}4}//> IF2: [A] (Cond:boolean, valtrue: = A, Valfalse: + a) a5IF2 (true, 1, 0)//> Run valtrue ...6 //| res2:int = 17IF2 (false, 1, 0)//> Run Valfalse ...8 //| res3:int = 09
The IF condition is strict in the parameters of the If-then-else function If2, and then and else are non-strict.
It can be seen whether the operation Valtrue or Valfalse are dependent on the result of the condition cond. But in any case the system will only operate one. Still, if Valtrue and Valfalse are both large and complex computations with thousands of lines of code, the Non-strict feature will save a lot of computational resources and improve the efficiency of system operation. In addition, the Non-strict feature is the basic requirement to implement an infinite data stream (Infinite stream), which is described in detail in the next section of the stream.
However, it is analyzed from another aspect: the non-strict parameter may be multiple operations inside the function, if this parameter is used more than once inside the function. Similarly, if this parameter is a large computation, it will result in a waste of resources. The lazy declaration in the Scala language solves the Non-strict parameter multiple operation problem. The lazy value declaration (lazy Val) not only delays the right-hand operation of an assignment expression, but also has the function of caching (cache): The right side of the arithmetic expression is actually made, and once the value is assigned, it is no longer recalculated. Let's try to make some modifications to the example above :
1 Def pair (x: = = int):(int, int) = { //> Pair: (x: = = int) (int, int)2 lazy Val y = x // No operation, not yet started using Y3 (y,y) // First y operation, the second one uses cached values 4 }
In this version we used a delay value (lazy val) y. When this function is called, the value operation of the parameter is calculated once using Y for the first time, then cached, and then using Y without repeating the calculation, using the cached value directly (cached value). You can look at the result of the function call:
1 Pair ({println ("Hello ..."); 5}) //> Hello ... 2 // | res1: (int, int) = (5,5)
A duplicate value (5,5) is also generated, but the parameter value operation is performed only once because there is only one line of hello ...
Functional Programming (11)-Deferred calculation-lazy evaluation