<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] # 第 4 章: 柯里化(curry) ## 不可或缺的 curry (譯者注:原標題是“Can't live if livin' is without you”,為英國樂隊 Badfinger 歌曲 *Without You* 中歌詞。) 我父親以前跟我說過,有些事物在你得到之前是無足輕重的,得到之后就不可或缺了。微波爐是這樣,智能手機是這樣,互聯網也是這樣——老人們在沒有互聯網的時候過得也很充實。對我來說,函數的柯里化(curry)也是這樣。 curry 的概念很簡單:只傳遞給函數一部分參數來調用它,讓它返回一個函數去處理剩下的參數。 你可以一次性地調用 curry 函數,也可以每次只傳一個參數分多次調用。 ```js var add = function(x) { return function(y) { return x + y; }; }; var increment = add(1); var addTen = add(10); increment(2); // 3 addTen(2); // 12 ``` 這里我們定義了一個 `add` 函數,它接受一個參數并返回一個新的函數。調用 `add` 之后,返回的函數就通過閉包的方式記住了 `add` 的第一個參數。一次性地調用它實在是有點繁瑣,好在我們可以使用一個特殊的 `curry` 幫助函數(helper function)使這類函數的定義和調用更加容易。 我們來創建一些 curry 函數享受下(譯者注:此處原文是“for our enjoyment”,語出自圣經)。 ```js var curry = require('lodash').curry; var match = curry(function(what, str) { return str.match(what); }); var replace = curry(function(what, replacement, str) { return str.replace(what, replacement); }); var filter = curry(function(f, ary) { return ary.filter(f); }); var map = curry(function(f, ary) { return ary.map(f); }); ``` 我在上面的代碼中遵循的是一種簡單,同時也非常重要的模式。即策略性地把要操作的數據(String, Array)放到最后一個參數里。到使用它們的時候你就明白這樣做的原因是什么了。 ```js match(/\s+/g, "hello world"); // [ ' ' ] match(/\s+/g)("hello world"); // [ ' ' ] var hasSpaces = match(/\s+/g); // function(x) { return x.match(/\s+/g) } hasSpaces("hello world"); // [ ' ' ] hasSpaces("spaceless"); // null filter(hasSpaces, ["tori_spelling", "tori amos"]); // ["tori amos"] var findSpaces = filter(hasSpaces); // function(xs) { return xs.filter(function(x) { return x.match(/\s+/g) }) } findSpaces(["tori_spelling", "tori amos"]); // ["tori amos"] var noVowels = replace(/[aeiou]/ig); // function(replacement, x) { return x.replace(/[aeiou]/ig, replacement) } var censored = noVowels("*"); // function(x) { return x.replace(/[aeiou]/ig, "*") } censored("Chocolate Rain"); // 'Ch*c*l*t* R**n' ``` 這里表明的是一種“預加載”函數的能力,通過傳遞一到兩個參數調用函數,就能得到一個記住了這些參數的新函數。 我鼓勵你使用 `npm install lodash` 安裝 `lodash`,復制上面的代碼放到 REPL 里跑一跑。當然你也可以在能夠使用 `lodash` 或 `ramda` 的網頁中運行它們。 ## 不僅僅是雙關語/咖喱 curry 的用處非常廣泛,就像在 `hasSpaces`、`findSpaces` 和 `censored` 看到的那樣,只需傳給函數一些參數,就能得到一個新函數。 用 `map` 簡單地把參數是單個元素的函數包裹一下,就能把它轉換成參數為數組的函數。 ```js var getChildren = function(x) { return x.childNodes; }; var allTheChildren = map(getChildren); ``` 只傳給函數一部分參數通常也叫做*局部調用*(partial application),能夠大量減少樣板文件代碼(boilerplate code)。考慮上面的 `allTheChildren` 函數,如果用 lodash 的普通 `map` 來寫會是什么樣的(注意參數的順序也變了): ```js var allTheChildren = function(elements) { return _.map(elements, getChildren); }; ``` 通常我們不定義直接操作數組的函數,因為只需內聯調用 `map(getChildren)` 就能達到目的。這一點同樣適用于 `sort`、`filter` 以及其他的高階函數(higher order function)(高階函數:參數或返回值為函數的函數)。 當我們談論*純函數*的時候,我們說它們接受一個輸入返回一個輸出。curry 函數所做的正是這樣:每傳遞一個參數調用函數,就返回一個新函數處理剩余的參數。這就是一個輸入對應一個輸出啊。 哪怕輸出是另一個函數,它也是純函數。當然 curry 函數也允許一次傳遞多個參數,但這只是出于減少 `()` 的方便。 ## 總結 curry 函數用起來非常得心應手,每天使用它對我來說簡直就是一種享受。它堪稱手頭必備工具,能夠讓函數式編程不那么繁瑣和沉悶。 通過簡單地傳遞幾個參數,就能動態創建實用的新函數;而且還能帶來一個額外好處,那就是保留了數學的函數定義,盡管參數不止一個。 下一章我們將學習另一個重要的工具:`組合`(compose)。 [第 5 章: 代碼組合(compose)](ch5.md) ## 練習 開始練習之前先說明一下,我們將默認使用 [ramda](http://ramdajs.com) 這個庫來把函數轉為 curry 函數。或者你也可以選擇由 losash 的作者編寫和維護的 [lodash-fp](https://github.com/lodash/lodash-fp)。這兩個庫都很好用,選擇哪一個就看你自己的喜好了。 你還可以對自己的練習代碼做[單元測試](https://github.com/llh911001/mostly-adequate-guide-chinese/tree/master/code/part1_exercises),或者把代碼拷貝到一個 REPL 里運行看看。 這些練習的答案可以在[本書倉庫](https://github.com/llh911001/mostly-adequate-guide-chinese/tree/master/code/part1_exercises/answers)中找到。 ```js var _ = require('ramda'); // 練習 1 //============== // 通過局部調用(partial apply)移除所有參數 var words = function(str) { return split(' ', str); }; // 練習 1a //============== // 使用 `map` 創建一個新的 `words` 函數,使之能夠操作字符串數組 var sentences = undefined; // 練習 2 //============== // 通過局部調用(partial apply)移除所有參數 var filterQs = function(xs) { return filter(function(x){ return match(/q/i, x); }, xs); }; // 練習 3 //============== // 使用幫助函數 `_keepHighest` 重構 `max` 使之成為 curry 函數 // 無須改動: var _keepHighest = function(x,y){ return x >= y ? x : y; }; // 重構這段代碼: var max = function(xs) { return reduce(function(acc, x){ return _keepHighest(acc, x); }, -Infinity, xs); }; // 彩蛋 1: // ============ // 包裹數組的 `slice` 函數使之成為 curry 函數 // //[1,2,3].slice(0, 2) var slice = undefined; // 彩蛋 2: // ============ // 借助 `slice` 定義一個 `take` curry 函數,該函數調用后可以取出字符串的前 n 個字符。 var take = undefined; ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看