Javascript Lambda的簡單實現(xiàn)

由來

今早面試看到一題是說使用js實現(xiàn).net中的where功能而账,當時腦子里一片空白启摄,只能回來腦補下(本來想將博客放到github及Blogger上凌外,但限于配置麻煩蹦玫,暫且放這里)啊片。真心不知道ES6以下的js支不支持 " => "只锻, 后來在chrome上測試下竟然支持,我沒有打開chrome對ES6的支持钠龙。這是一個簡單實現(xiàn)炬藤,主要使用到了eval,暫時沒有考慮其性能碴里,同時實現(xiàn)了 select 及distinct方法沈矿,歡迎大牛指正。既然語法支持那就可以對傳入的參數(shù)做進一步處理咬腋,首先將參數(shù)轉為字符串然后使用正則提取變量及表達式羹膳,到使用eval執(zhí)行語句時再將表達式中的參數(shù)替換為指定的值。以下代碼還可以重構根竿,可以考慮寫一個方法專門解析表達式陵像、增加表達式正確性校驗等等。

        Array.prototype.where = function(){
            var args = arguments[0].toString();
            var matches = args.match(/(\w)(\s+)?=>(.*)+/);
            if(!matches){
                console.error('invalid expression.');
                return;  
            } 
            var name = matches[1];
            var expression = matches[3];
            
            if(!this) return [];
            var result=[];
            this.forEach(function(value, index, array) {
                eval('var rpr=/'+ name + '/g');
               var newexp = expression.replace(rpr,value);
               var res = eval(newexp);
               if(res){
                 result.push(value);
               }
            });
            return result;
        };
        Array.prototype.select = function(){
            var args = arguments[0].toString();
            var matches = args.match(/(\w)(\s+)?=>(.*)+/);
            if(!matches){
                console.error('invalid expression .');
                return;  
            } 
            var name = matches[1];
            var expression = matches[3];
            matches = expression.match(/\.(\w+)/);
            if(!matches)    {
                console.error('invalid expression .');
                return;  
            } 
            var property = matches[1];
            var result=[];
            this.forEach(function(value, index, array) {
               if(value[property]){
                 result.push(value[property]);
               }
            });
            return result;
        };
        Array.prototype.distinct = function(){
            var args = arguments[0].toString();
            var matches = args.match(/(\w)(\s+)?=>(.*)+/);
            if(!matches){
                console.error('invalid expression .');
                return;  
            } 
            var name = matches[1];
            var expression = matches[3];
            matches = expression.match(/\.(\w+)/);
            if(!matches)    {
                console.error('invalid expression .');
                return;  
            } 
            var property = matches[1];
            var result=[];
            this.forEach(function(value, index, array) {
               if(value[property]){
                 var add = true;
                 for (var i = 0; i < result.length; i++) {
                    if(value[property]==result[i][property]){ 
                        add = false; 
                        break;  
                    }
                 }
                 if(add) result.push(value);
               }
            });
            return result;
        };
        var mres = [20,33,40,89,55].where(x=>x%5==0);
        var seles = [{age:423},{age:120},{name:'leo',age:80}].select(a=>a.age);
        var distic = [{age:423},{age:120},{age:120},
                     {name:'leo',age:80},{name:'leo',age:80}].distinct(a=>a.age);
        console.log(mres);

結果

1.png

性能測試

昨天看一個篇不錯的文章里面使用了10萬數(shù)據來測試eval及function寇壳,看到這個那我也來試試醒颖。代碼修改如下:

        var arrs = [],models=[];
        for (var i = 0; i < 100000; i++) {
            arrs.push(i);
            models.push({age:i,name:'name'+i});
        }
        
        console.time("where");
        var mres = arrs.where(x=>x%5==0);
        console.timeEnd("where");
        console.time("select");
        var seles = models.select(a=>a.age);
        console.timeEnd("select");
        console.time("distinct");
        var distic = models.distinct(a=>a.age);
        console.timeEnd("distinct");

相比distinct,其他兩個 很快就能看到結果壳炎,過了7-8分鐘還是沒有結果泞歉,后來看了下代碼,自己真是笑死匿辩。
distinct使用了一個最壞的情況來運行腰耙,arrs中沒有重復數(shù)據,時間復雜度是O(n2)铲球,這么看沒有個把鐘出不來. 那就先讓他自己運行吧挺庞,剛好和妹子有約,晚上回來在看稼病。晚上11點半到家选侨,結果如下:

all.png

結果和預期差不多,一個半小時溯饵,chrome按F12在debug模式下會慢些侵俗。既然如此那就來調整下,一個是where的實現(xiàn)丰刊,一個distinct的是實現(xiàn)隘谣,select功能少暫時可以不用考慮。

where

先看使用eval的實現(xiàn):


whereeval.png

然后是function的實現(xiàn):

wherefunc.png

最后看結果:

whereresult.png

從結果看這差距不是一般的大。

distinct

這個然我想起了之前看到的位圖算法寻歧,用一個bit位來記錄某項記錄是否已存在掌栅,我們可以使用的最小數(shù)據類型只能是float及int,可以考慮使用強類型數(shù)組 Uint32Array码泛。這里有個問題是我們事先并不能知道某一個數(shù)的大小猾封,有可能該數(shù)據所在的位置已經超出bit_arr的界限,所以這個需要做判斷噪珊。如果超出則增加bit_arr的長度晌缘。然后我的實現(xiàn)如下:

(function(){
        var bit_arr ;
        var get = function(index,offset){
            var value = bit_arr[index]>>offset;
            return value & 0x01;
        };
        var set = function(index,offset){
            bit_arr[index] = bit_arr[index]+(1<<offset); 
        }
       
        window.distinct = function(arr){
            var count = parseInt(arr.length/32) + 1;
            bit_arr = new Uint32Array(count);
            for (var i = 0; i < count; i++) {
                bit_arr[i]=0;
            }
            var results = [];
            arr.forEach(function(value,index,array){
                var arr_index = parseInt(value/32);
                var offset = value%32;
                if(!bit_arr[arr_index]) bit_arr[arr_index] = 0;

                var bit = get(arr_index,offset);
                if(bit == 0){
                    set(arr_index,offset);
                    results.push(value);
                }
            });
            return results;
        };
    })();

測試代碼如下:

        var arrs = [],models=[];
        for (var i = 0; i < 20000; i++) {
            arrs.push(i);
            // models.push({age:i,name:'name'+i});
        }
        for (var i = 0; i < 20000; i++) {
            arrs.push(i);
        }
        for (var i = 0; i < 40000; i++) {
            arrs.push(i*2);
        }
        for (var i = 0; i < 20000; i++) {
            arrs.push(i*3);
        }
        console.time("distinct");
        distinct(arrs);
        console.timeEnd("distinct");

測試結果:

distinct.png

結果還明顯,我多次測試其時間都在130ms左右痢站。

<center>總結</center>

其實很多時候只要有一個點子一個想法都可以去嘗試磷箕,做出來后定會有收獲。有關lambda部分還有很多功能可以實現(xiàn)阵难,像前面提到的那位博主有考慮到緩存function岳枷,不過他的lambda表達式是使用字符串包含的,感覺不太雅觀呜叫。CSDN的編輯器太操蛋空繁,竟然沒有自動保存草稿的功能,害我重寫朱庆,哎...真不夠專業(yè)盛泡。還是馬克飛象好些。先這樣了娱颊,后續(xù)有想法再補充饭于。

個人github博客地址參見 https://blog.magicleox.com/

有關eval性能的文章:
http://www.nowamagic.net/librarys/veda/detail/1627

https://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市维蒙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌果覆,老刑警劉巖颅痊,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異局待,居然都是意外死亡斑响,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門钳榨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舰罚,“玉大人,你說我怎么就攤上這事薛耻∮眨” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長饲漾。 經常有香客問我蝙搔,道長,這世上最難降的妖魔是什么考传? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任吃型,我火速辦了婚禮,結果婚禮上僚楞,老公的妹妹穿的比我還像新娘勤晚。我一直安慰自己,他們只是感情好泉褐,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布赐写。 她就那樣靜靜地躺著,像睡著了一般兴枯。 火紅的嫁衣襯著肌膚如雪血淌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天财剖,我揣著相機與錄音悠夯,去河邊找鬼。 笑死躺坟,一個胖子當著我的面吹牛沦补,可吹牛的內容都是我干的。 我是一名探鬼主播咪橙,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼夕膀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了美侦?” 一聲冷哼從身側響起产舞,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎菠剩,沒想到半個月后易猫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡具壮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年准颓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棺妓。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡攘已,死狀恐怖,靈堂內的尸體忽然破棺而出怜跑,到底是詐尸還是另有隱情样勃,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站彤灶,受9級特大地震影響看幼,放射性物質發(fā)生泄漏。R本人自食惡果不足惜幌陕,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一诵姜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧搏熄,春花似錦棚唆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至止后,卻和暖如春瞎惫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背译株。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工瓜喇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人歉糜。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓乘寒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親匪补。 傳聞我的和親對象是個殘疾皇子伞辛,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內容