The source interpretation of lazy evaluation--lodash

Source: Internet
Author: User
Objective

One of the reasons for Lodash's popularity is its excellent computational performance. And its performance can have so outstanding performance, much of it is derived from its use of the algorithm-lazy evaluation.
This article will describe the principle and implementation of lazy evaluation in Lodash source code.

An analysis of the principle of inertia evaluation

Lazy Evaluation (lazy Evaluation), also known as lazy computing, lazy evaluation, called call to Demand (Call-by-need), is a concept in computer programming, its purpose is to minimize the computer to do the work .
The parameters in the lazy evaluation are not evaluated until they are needed. This kind of program actually starts from the end and executes backwards . It will determine what it needs to return and continue backwards to determine what values are required to do so.

What are the following how to speed up lo-dashx100? Introducing Lazy Evaluation. (How to improve the Lo-dash?) An introduction to lazy computing) The example in this article shows the lazy evaluation visually.

function priceLt(x) {   return function(item) { return item.price < x; };}var gems = [   { name: 'Sunstone', price: 4 },   { name: 'Amethyst', price: 15 },   { name: 'Prehnite', price: 20},   { name: 'Sugilite', price: 7  },   { name: 'Diopside', price: 3 },   { name: 'Feldspar', price: 13 },   { name: 'Dioptase', price: 2 },   { name: 'Sapphire', price: 20 }]; var chosen = _(gems).filter(priceLt(10)).take(3).value();

The purpose of the program is to filter the data set to gems Select 3 price less than 10 of the data.

1.1 General Procedure

If you throw away lodash this library and let you do it in a normal way, you can do it in the var chosen = _(gems).filter(priceLt(10)).take(3) following ways:
_(gems)Get the data set and cache it up.
Execute the filter method again, iterate over the gems array (length 10), and remove the data that matches the criteria:

[   { name: 'Sunstone', price: 4 },   { name: 'Sugilite', price: 7  },   { name: 'Diopside', price: 3 },   { name: 'Dioptase', price: 2 }]

Then, execute take the method to extract the first 3 data.

[   { name: 'Sunstone', price: 4 },   { name: 'Sugilite', price: 7  },   { name: 'Diopside', price: 3 }]

The total number of traversal is: 10+3 .
An example diagram of execution is as follows:

1.2 Lazy Evaluation Procedure

The common practice has a problem: Each method does everything, does not coordinate to waste a lot of resources.
It would be better if we could write down the things we want to do first, and then wait until the data is really out, and then use the fewest number of times to achieve the goal.
This is how lazy computing is done.
Here are the ideas for implementation:

    • _(gems)Get the data set, cache it.
    • filtercome up with a method, write it down first
    • takecome up with a method, write it down first
    • Come value up with a way to explain the time
    • Take the little books out and look at the requirements: to take out 3 numbers, price<10
    • Use filter the method to judge the priceLt data
[    { name: 'Sunstone', price: 4 }, => priceLt裁决 => 符合要求,通过 => 拿到1个    { name: 'Amethyst', price: 15 }, => priceLt裁决 => 不符合要求    { name: 'Prehnite', price: 20}, => priceLt裁决 => 不符合要求    { name: 'Sugilite', price: 7  }, => priceLt裁决 => 符合要求,通过 => 拿到2个    { name: 'Diopside', price: 3 }, => priceLt裁决 => 符合要求,通过 => 拿到3个 => 够了,收工!    { name: 'Feldspar', price: 13 },    { name: 'Dioptase', price: 2 },    { name: 'Sapphire', price: 20 }]

As shown above, the results are taken only 5 times.
An example diagram of execution is as follows:

1.3 Summary

The characteristics of lazy calculation can be obtained from the above example:

    • delay calculation , the calculation to be done first cache, do not execute
    • Data Pipeline , data through the "adjudication" method, in this similar security procedures, the clearance of operations, and finally left only the data that meets the requirements
    • Trigger Time , method cache, then a method is required to trigger execution. Lodash is value the use of methods that inform the real start of the calculation
Second, the realization of the lazy evaluation

According to the above characteristics, I will lodash the lazy evaluation of the implementation of the following parts of the extraction:

2.1 Caching for deferred computation

Implementation _(gems) . I'm here for the sake of clarity, using lazy(gems) substitution.

var MAX_ARRAY_LENGTH = 4294967295; // 最大的数组长度// 缓存数据结构体function LazyWrapper(value){    this.__wrapped__ = value;    this.__iteratees__ = [];    this.__takeCount__ = MAX_ARRAY_LENGTH;}// 惰性求值的入口function lazy(value){    return new LazyWrapper(value);}
    • this.__wrapped__Cache data
    • this.__iteratees__How to "adjudicate" in a cached data pipeline
    • this.__takeCount__Record the number of data sets required to meet the requirements

In this way, a basic structure is completed.

2.2 Implementation filterMethod
var LAZY_FILTER_FLAG = 1; // filter方法的标记// 根据 筛选方法iteratee 筛选数据function filter(iteratee){    this.__iteratees__.push({        'iteratee': iteratee,        'type': LAZY_FILTER_FLAG    });    return this;}// 绑定方法到原型链上LazyWrapper.prototype.filter = filter;

filtermethod to cache the ruling method iteratee . One important point here is the type of record that needs to be recorded iteratee type .
Because in lodash , there are map methods to filter the data, but also to pass an adjudication method iteratee . Because filter methods and map methods are filtered differently, tags are used type .
Here's another tip:

(function(){    // 私有方法    function filter(iteratee){        /* code */    }    // 绑定方法到原型链上    LazyWrapper.prototype.filter = filter;})();

The prototype method is first declared with a normal function and then bound to the prototype. If you need to use the tool internally filter , use a private, declarative method.
The advantage is that external changes LazyWrapper.prototype.filter , within the tool, have no effect.

2.3 Implementation takeMethod
// 截取n个数据function take(n){    this.__takeCount__ = n;    return this;};LazyWrapper.prototype.take = take;
2.4 implementation valueMethod
Lazy evaluation Function LazyValue () {var array = this.__wrapped__;    var length = Array.Length;    var resindex = 0;    var takecount = this.__takecount__;    var iteratees = this.__iteratees__;    var iterlength = iteratees.length;    var index =-1;    var dir = 1;    var result = [];        Label Statement Outer:while (length--&& Resindex < Takecount) {//outer loop array to be processed index = = dir;        var iterindex =-1;        var value = Array[index];            while (++iterindex < iterlength) {//inner loop processing on chain method var data = Iteratees[iterindex];            var iteratee = data.iteratee;            var type = Data.type;            var computed = iteratee (value);                Handling data that does not meet the requirements if (!computed) {if (type = = Lazy_filter_flag) {continue outer;                }else{break outer;    }}}//Through the inner loop, conforming to the required data result[resindex++] = value; } return result;}lazywrapper.prototype.value = LazyValue; 

One of the key points here is: tag statements

    outer:    while(length-- && resIndex < takeCount){        // 外层循环待处理的数组        index += dir;        var iterIndex = -1;        var value = array[index];        while(++iterIndex < iterLength){            // 内层循环处理链上的方法            var data = iteratees[iterIndex];            var iteratee = data.iteratee;            var type = data.type;            var computed = iteratee(value);            // 处理数据不符合要求的情况            if(!computed){                if(type == LAZY_FILTER_FLAG){                    continue outer;                }else{                    break outer;                }            }        }        // 经过内层循环,符合要求的数据        result[resIndex++] = value;    }

The current method of data pipeline implementation, is actually the inner layer of the while loop. The iteratees current data is adjudicated by removing the ruling method in the cache value .
If the result of the award is not met, that is false . At this time, there is no need to use the subsequent adjudication method to judge. Instead, you should jump out of the current loop.
And if you break jump out of the inner Loop, the outer loop result[resIndex++] = value; will still be executed, which is what we don't want to see.
It is correct to jump out of both inside and outside loops and continue the outer loop.
Tag statement, just to meet this requirement.

2.5 Small Detection
var testArr = [1, 19, 30, 2, 12, 5, 28, 4];lazy(testArr)    .filter(function(x){        console.log('check x='+x);        return x < 10    })    .take(2)    .value();// 输出如下:check x=1check x=19check x=30check x=2// 得到结果: [1, 2]
2.6 Summary

The entire lazy evaluation of the implementation, the focus is on the data pipeline this block. Well, the label statement here is magical. In fact, the way to achieve is not just the current. However, the main point is also mentioned in the previous three. It's easy to master the essence and make it work.

Conclusion

Lazy evaluation, is I read the lodash source code, found the biggest shining spot.
At the beginning of the lazy evaluation is not very understanding, want to see the implementation of JavaScript, but the Internet only found a document mentioned above.
The rest of the choice is to perform a split analysis of the Lodash. Also because of this, only the birth of this article.
I hope this article will be of some help to you. If you can, give a star:)

Finally, attach the simplified version of this article lazy.js full Source:

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.