• <noscript id="ggggg"><dd id="ggggg"></dd></noscript>
    <small id="ggggg"></small> <sup id="ggggg"></sup>
    <noscript id="ggggg"><dd id="ggggg"></dd></noscript>
    <tfoot id="ggggg"></tfoot>
  • <nav id="ggggg"><cite id="ggggg"></cite></nav>
    <nav id="ggggg"></nav>
    成人黃色A片免费看三更小说,精品人妻av区波多野结衣,亚洲第一极品精品无码,欧美综合区自拍亚洲综合,久久99青青精品免费观看,中文字幕在线中字日韩 ,亚洲国产精品18久久久久久,黄色在线免费观看

    JavaScript 中匿名函數的遞歸調用

    2016-9-27    藍藍設計的小編

    如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里

    不管是什么編程語言,相信稍微寫過幾行代碼的同學,對遞歸都不會陌生。 以一個簡單的階乘計算為例:

    function factorial(n) { if (n <= 1) { return 1;
        } else { return n * factorial(n-1);
        }
    }

    我們可以看出,遞歸就是在函數內部調用對自身的調用。 那么問題來了,我們知道在Javascript中,有一類函數叫做匿名函數,沒有名稱,怎么調用呢?當然你可以說,可以把匿名函數賦值給一個常量:

    const factorial = function(n){ if (n <= 1) { return 1;
        } else { return n * factorial(n-1);
        }
    }

    這當然是可以的。但是對于一些像,函數編寫時并不知道自己將要賦值給一個明確的變量的情況時,就會遇到麻煩了。如:

    (function(f){
        f(10);
    })(function(n){ if (n <= 1) { return 1;
        } else { return n * factorial(n-1);//太依賴于上下文變量名 }
    }) //Uncaught ReferenceError: factorial is not defined(…)

    那么存不存在一種完全不需要這種給予準確函數名(函數引用變量名)的方式呢?

    arguments.callee

    我們知道在任何一個function內部,都可以訪問到一個叫做arguments的變量。

    (function(){console.dir(arguments)})(1,2)

    1.png

    打印出這個arguments變量的細節,可以看出他是Arguments的一個實例,而且從數據結構上來講,他是一個類數組。他除了類數組的元素成員和length屬性外,還有一個callee方法。 那么這個callee方法是做什么的呢?我們來看下MDN

    callee 是 arguments 對象的屬性。在該函數的函數體內,它可以指向當前正在執行的函數。當函數是匿名函數時,這是很有用的, 比如沒有名字的函數表達式 (也被叫做”匿名函數”)。

    哈哈,很明顯這就是我們想要的。接下來就是:

    (function(f){ console.log(f(10));
    })(function(n){ if (n <= 1) { return 1;
        } else { return n * arguments.callee(n-1);
        }
    }) //output: 3628800

    但是還有一個問題,MDN的文檔里明確指出

    警告:在 ECMAScript 第五版 (ES5) 的 嚴格模式 中禁止使用 arguments.callee()。

    哎呀,原來在ES5的use strict;中不給用啊,那么在ES6中,我們換個ES6的arrow function寫寫看:

    ((f) => console.log(f(10)))( (n) => n <= 1? 1: arguments.callee(n-1)) //Uncaught ReferenceError: arguments is not defined(…)

    有一定ES6基礎的同學,估計老早就想說了,箭頭函數就是個簡寫形式的函數表達式,并且它擁有詞法作用域的this值(即不會新產生自己作用域下的thisargumentssuper 和 new.target等對象),且都是匿名的。

    那怎么辦呢?嘿嘿,我們需要借助一點FP的思想了。

    Y組合子

    關于Y Combinator的文章可謂數不勝數,這個由師從希爾伯特的著名邏輯學家Haskell B.Curry(Haskell語言就是以他命名的,而函數式編程語言里面的Curry手法也是以他命名)“發明”出來的組合算子(Haskell是研究組合邏輯(combinatory logic)的)仿佛有種神奇的魔力,它能夠算出給定lambda表達式(函數)的不動點。從而使得遞歸成為可能。

    這里需要告知一個概念不動點組合子

    不動點組合子(英語:Fixed-point combinator,或不動點算子)是計算其他函數的一個不動點的高階函數。

    函數f的不動點是一個值x使得f(x) = x。例如,0和1是函數 f(x) = x^2 的不動點,因為 0^2 = 0而 1^2 = 1。鑒于一階函數(在簡單值比如整數上的函數)的不動點是個一階值,高階函數f的不動點是另一個函數g使得f(g) = g。那么,不動點算子是任何函數fix使得對于任何函數f都有

    f(fix(f)) = fix(f). 不動點組合子允許定義匿名的遞歸函數。它們可以用非遞歸的lambda抽象來定義.

    在無類型lambda演算中眾所周知的(可能是最簡單的)不動點組合子叫做Y組合子。

    接下來,我們通過一定的演算推到下這個Y組合子。

    // 首先我們定義這樣一個可以用作求階乘的遞歸函數 const fact = (n) => n<=1?1:n*fact(n-1)  
    console.log(fact(5)) //120 // 既然不讓這個函數有名字,我們就先給這個遞歸方法一個叫做self的代號 // 首先是一個接受這個遞歸函數作為參數的一個高階函數 const fact_gen = (self) => (n) => n<=1?1:n*self(n-1)  
    console.log(fact_gen(fact)(5)) //120 // 我們是將遞歸方法和參數n,都傳入遞歸方法,得到這樣一個函數 const fact1 = (self, n) => n<=1?1:n*self(self, n-1)  
    console.log(fact1(fact1, 5)) //120 // 我們將fact1 柯理化,得到fact2 const fact2 = (self) => (n) => n<=1?1:n*self(self)(n-1)  
    console.log(fact2(fact2)(5)) //120 // 驚喜的事發生了,如果我們將self(self)看做一個整體 // 作為參數傳入一個新的函數: (g)=> n<= 1? 1: n*g(n-1) const fact3 = (self) => (n) => ((g)=>n <= 1?1:n*g(n-1))(self(self))  
    console.log(fact3(fact3)(5)) //120 // fact3 還有一個問題是這個新抽離出來的函數,是上下文有關的 // 他依賴于上文的n, 所以我們將n作為新的參數 // 重新構造出這么一個函數: (g) => (m) => m<=1?1:m*g(m-1) const fact4 = (self) => (n) => ((g) => (m) => m<=1?1:m*g(m-1))(self(self))(n)  
    console.log(fact4(fact4)(5)) // 很明顯fact4中的(g) => (m) => m<=1?1:m*g(m-1) 就是 fact_gen // 這就很有意思啦,這個fact_gen上下文無關了, 可以作為參數傳入了 const weirdFunc = (func_gen) => (self) => (n) => func_gen(self(self))(n)  
    console.log(weirdFunc(fact_gen)(weirdFunc(fact_gen))(5)) //120 // 此時我們就得到了一種Y組合子的形式了 const Y_ = (gen) => (f) => (n)=> gen(f(f))(n) // 構造一個階乘遞歸也很easy了 const factorial = Y_(fact_gen)  
    console.log(factorial(factorial)(5)) //120 // 但上面這個factorial并不是我們想要的 // 只是一種fact2,fact3,fact4的形式 // 我們肯定希望這個函數的調用是factorial(5) // 沒問題,我們只需要把定義一個 f' = f(f) = (f)=>f(f) // eg. const factorial = fact2(fact2) const Y = gen => n => (f=>f(f))(gen)(n)  
    console.log(Y(fact2)(5)) //120  console.log(Y(fact3)(5)) //120  console.log(Y(fact4)(5)) //120

    推導到這里,是不是已經感覺到脊背嗖涼了一下,反正筆者我第一次接觸在康托爾、哥德爾、圖靈——永恒的金色對角線這篇文章里接觸到的時候,整個人瞬間被這種以數學語言去表示程序的方式所折服。

    來,我們回憶下,我們最終是不是得到了一個不定點算子,這個算子可以找出一個高階函數的不動點f(Y(f)) = Y(f)。 將一個函數傳入一個算子(函數),得到一個跟自己功能一樣,但又并不是自己的函數,這個說法有些拗口,但又味道十足。

    好了,我們回到最初的問題,怎么完成匿名函數的遞歸呢?有了Y組合子就很簡單了:

    /*求不動點*/ (f => f(f)) /*以不動點為參數的遞歸函數*/ (fact => n => n <= 1 ? 1 : n * fact(fact)(n - 1)) /*遞歸函數參數*/ (5) // 120

    曾經看到過一些說法是”最讓人沮喪是,當你推導出它(Y組合子)后,完全沒法兒通過只看它一眼就說出它到底是想干嘛”,而我恰恰認為這就是函數式編程的魅力,也是數學的魅力所在,精簡優雅的公式,背后隱藏著復雜有趣的推導過程。

    2.jpg

    總結

    務實點兒講,匿名函數的遞歸調用,在日常的js開發中,用到的真的很少。把這個問題拿出來講,主要是想引出對arguments的一些講解和對Y組合子這個概念的一個普及。

    但既然講都講了,我們真的用到的話,該怎么選擇呢?來,我們喜聞樂見的benchmark下: 分別測試:

    // fact  fact(10) // Y (f => f(f))(fact => n => n <= 1 ? 1 : n * fact(fact)(n - 1))(10) // Y' const fix = (f) => f(f) const ygen = fix(fact2)  
    ygen(10) // callee (function(n) {n<=1?1:n*arguments.callee(n-1)})(10)

    環境:Macbook pro(2.5 GHz Intel Core i7), node-5.0.0(V8:4.6.85.28) 結果:

    fact x 18,604,101 ops/sec ±2.22% (88 runs sampled)

    Y x 2,799,791 ops/sec ±1.03% (87 runs sampled)

    Y’ x 3,678,654 ops/sec ±1.57% (77 runs sampled)

    callee x 2,632,864 ops/sec ±0.99% (81 runs sampled)

    可見Y和callee的性能相差不多,因為需要臨時構建函數,所以跟直接的fact遞歸調用有差不多一個數量級的差異,將不定點函數算出后保存下來,大概會有一倍左右的性能提升。

     

    藍藍設計www.lzhte.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服務 

     

    日歷

    鏈接

    個人資料

    藍藍設計的小編 http://www.lzhte.cn

    存檔

    主站蜘蛛池模板: 亚洲男人天堂2018| 91破解版在线亚洲| 亚洲中文字幕一区二区| 亚洲最大有声小说AV网| 望奎县| 九九久久精品国产免费看小说| 国产美女视频一区二区三区 | 亚洲—本道中文字幕久久66| 五月狠狠亚洲小说专区| 一区国产传媒国产精品 | www操| 亚洲综合久久一区二区| 狠狠做五月深爱婷婷伊人| 亚洲中文无码天堂一区二区三区| 免费人成又黄又爽的视频网站| 中美日韩在线网免费毛片视频| 亚洲 自拍 另类小说综合图区| 曰韩亚洲AV人人夜夜澡人人爽| 亚洲综合久久成人av| 人妻少妇看A偷人无码电影| 新乐市| 久久99国产伦精品免费| 精品免费人伦一区二区三区蜜桃| 国产精品久久久久9999赢消 | 青青青伊人色综合久久| 大香伊人久久精品一区二区| 思思99热精品在线| 欧美mv日韩mv国产mv网站| 午夜高清福利在线观看| 欧美日韩一区二区三区麻豆| 国内精品大秀视频日韩精品| 日韩精品国产专区一区| 玩弄放荡人妇系列av在线网站| 日产欧美国产日韩精品 | 亚洲综合一区精品自拍| 色综合天天综合网在线| 男男视频在线一区二区| av最新版天堂在资源在线| 一本久道久久综合狠狠躁av| 亚洲AV无码专区在线观看素人| 欧美精品AⅤ在线视频|