成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

ramda.js的compose源碼解析

ZweiZhao / 917人閱讀

摘要:至此,簡化版的就完成了??梢钥闯觯膶崿F(xiàn)從頭到尾都是函數(shù)式編程的思想,下一篇文章打算結(jié)合社區(qū)的一道問答題來介紹一下如何用函數(shù)式思想來解決問題。我也是初學(xué)函數(shù)式,有什么說的不準(zhǔn)確的地方希望多多指正。

前言

上一篇文章介紹了javascript中的compose函數(shù)的實現(xiàn),我是用了遞歸的思想去讓函數(shù)依次執(zhí)行,lodash中是用了迭代的思想依次執(zhí)行函數(shù),但實現(xiàn)了以后我還是覺得有些別扭,仔細(xì)想想,我們實現(xiàn)的是一個函數(shù)式編程用到的函數(shù),但是實現(xiàn)的方法還是太命令式了,函數(shù)還是命令式的執(zhí)行,通俗點說,還是太把函數(shù)當(dāng)成函數(shù)了,在我的理解中,函數(shù)和普通變量沒什么區(qū)別,只是執(zhí)行的方法不一樣,一旦賦予了函數(shù)這個執(zhí)行的屬性,我們就可以完全將函數(shù)當(dāng)成普通變量去對待。

函數(shù)和普通變量沒什么區(qū)別,只是需要偶爾執(zhí)行一下

實現(xiàn) 1.函數(shù)世界的加號

舉個例子

1 + 2 = 3
"a" + "b" = "ab"
func1 "+" func2 -> func3

前兩個例子就是普通變量的操作,最后一個例子是函數(shù)的操作,本質(zhì)上看來,沒有任何區(qū)別,兩個函數(shù)作用的結(jié)果就是生成一個函數(shù),只不過在函數(shù)的世界里,這個加號的意義就是如何變換生成一個新的函數(shù),回到compose來,在compose中,加號的意義就是把一個函數(shù)的執(zhí)行結(jié)果當(dāng)成下一個函數(shù)的輸入,最后在生成一個函數(shù),就像下面這樣

var fn = (func1, func2) => (...args) => func2.call(this, func1.apply(this, args))

在這個例子里面,func1的執(zhí)行結(jié)果就是func2的參數(shù),并且生成了一個新的函數(shù)fn,我們給這個fn傳遞參數(shù),它就會作為func1的參數(shù)來啟動執(zhí)行,最后得到了函數(shù)依次執(zhí)行的效果,這就是最簡單的compose,這個函數(shù)就是ramda.js實現(xiàn)compsoe需要的第一個函數(shù)_pipe

var _pipe = (f, g) => (...args) => g.call(this, f.apply(this, args))

_pipe就定義了compose中所謂加號的意義了。

2."不一樣的"reduce

在這里提到了reduce,是不是有一點感覺,reduce的作用就是讓一個數(shù)組不斷的執(zhí)行下去,所以肯定能和咱們這個compose有點聯(lián)系,先舉個reduce最常用的例子,求數(shù)組的和

var a = [1,2,3,4,5]
a.reduce((x, y) => x + y, 0)

這個就是不斷的將兩個數(shù)求和,生成一個新的數(shù),再去和下一個數(shù)求和,最后得到15,下面想一下,如果把數(shù)字換成函數(shù)會怎么樣,兩個函數(shù)結(jié)合生成一個新的函數(shù),這個結(jié)合法則就使用上面的_pipe,這個新的函數(shù)再去結(jié)合下一個函數(shù),直到最后一個函數(shù)執(zhí)行完,我們得到的還是函數(shù),我們前面說了,函數(shù)知識偶爾需要執(zhí)行一下,這個函數(shù)的生成和執(zhí)行過程是反向遞歸的過程。利用這個思想,就可以寥寥幾行(甚至只需要一行)就寫出來這個非常函數(shù)式的compose

var reverse = arr => arr.reverse()
var _pipe = (f, g) => (...args) => g.call(this, f.apply(this, args));
var compose = (...args) => reverse(args).reduce(_pipe, args.shift())

舉個例子驗證一下,我們把首個函數(shù)做多元處理,再upperCase,再repeat

var classyGreeting = (firstName, lastName) => "The name"s " + lastName + ", " + firstName + " " + lastName
var toUpper = str => str.toUpperCase()
var repeat = str => str.repeat(2)
var result = compose(repeat, toUpper, classyGreeting)("dong", "zhe")
// THE NAME"S ZHE, DONG ZHETHE NAME"S ZHE, DONG ZHE

我在這里把函數(shù)生成過程分析一下

首先我們用_pipe組合classyGreeting,toUpper

f1 = _pipe(classyGreeting, toUpper)
f1 = (...args) => toUpper.call(this, classyGreeting.apply(this, args))

_pipe繼續(xù)結(jié)合f1, repeat

f2 = _pipe(f1, repeat)
f2 = (...args) => repeat.call(this, f1.apply(this, args))

函數(shù)的執(zhí)行過程就會將參數(shù)層層傳遞到最里面的classyGreeting開始執(zhí)行,從而完成函數(shù)的依次執(zhí)行。ramda.js自己實現(xiàn)了reduce,不僅支持?jǐn)?shù)組的reduce,還支持多種數(shù)據(jù)結(jié)構(gòu)的reduce,(兼容性也更好?),下一步來分析是如何自己實現(xiàn)數(shù)組的reduce的,可與看出,自己分離出來邏輯之后,函數(shù)的執(zhí)行過程和組合的規(guī)則部分將分離的更徹底。

3.自己寫一個reduce

reduce接受三個參數(shù),執(zhí)行函數(shù),初始值,執(zhí)行隊列(可以不止為一個數(shù)組),返回一個針對這些參數(shù)的reduce處理,這里只寫數(shù)組部分(_arrayReduce),源碼中還包含了關(guān)于迭代器的_iterableReduce 等等,而且ramda.js對執(zhí)行函數(shù)也有一層對象封裝,擴展了函數(shù)的功能

var reduce = (fn, acc, list) => (fn = _xwrap(fn), _arrayReduce(fn, acc, list))

在寫_arrayReduce之前,先來看一下函數(shù)的對象封裝_xwrap

var _xwrap = (function(){
    function XWrap(fn) {
        this.f = fn;
    }
    XWrap.prototype["@@transducer/init"] = function() {
        throw new Error("init not implemented on XWrap");
    };
    XWrap.prototype["@@transducer/result"] = function(acc) {
        return acc;
    };
    XWrap.prototype["@@transducer/step"] = function(acc, x) {
        return this.f(acc, x);
    };
    return function _xwrap(fn) { return new XWrap(fn); };
})()

其實就是對函數(shù)執(zhí)行狀態(tài)做了一個分類管理
@@transducer/step 這種狀態(tài)認(rèn)為是一種過程狀態(tài)
@@transducer/result 這種狀態(tài)被認(rèn)為是一種結(jié)果狀態(tài)
這種狀態(tài)管理通過對象也是合情合理的
最后再來完成_arrayReduce,就很簡單了,這個函數(shù)只是專心一件事情,就是寫reduce的過程規(guī)則。

var _arrayReduce = (xf, acc, list) => {
    var idx = 0
    var len = list.length
    while (idx < len) {
        acc = xf["@@transducer/step"](acc, list[idx]);
        idx += 1;
    }
    return xf["@@transducer/result"](acc);
}

至此,ramda.js簡化版的reduce就完成了。

4.其他一些功能

tail用來分離初始值和執(zhí)行隊列的,因為初始函數(shù)是多元的(接收多個參數(shù)),執(zhí)行隊列都是一元(接收一個參數(shù))的,分離還是有必要的

var tail = arr => arr.slice(1)

reverse改變執(zhí)行順序

var reverse = arr => arr.reverse()  

_arity我把源代碼貼出來,我也不知道為什么這樣做,可能是明確指定參數(shù)吧,因為reduce生成的函數(shù)是可以接受多個參數(shù)的,_arity就是處理這個函數(shù)的

var _arity = (n, fn) => {
    switch (n) {
    case 0: return function() { return fn.apply(this, arguments); };
    case 1: return function(a0) { return fn.apply(this, arguments); };
    case 2: return function(a0, a1) { return fn.apply(this, arguments); };
    case 3: return function(a0, a1, a2) { return fn.apply(this, arguments); };
    case 4: return function(a0, a1, a2, a3) { return fn.apply(this, arguments); };
    case 5: return function(a0, a1, a2, a3, a4) { return fn.apply(this, arguments); };
    case 6: return function(a0, a1, a2, a3, a4, a5) { return fn.apply(this, arguments); };
    case 7: return function(a0, a1, a2, a3, a4, a5, a6) { return fn.apply(this, arguments); };
    case 8: return function(a0, a1, a2, a3, a4, a5, a6, a7) { return fn.apply(this, arguments); };
    case 9: return function(a0, a1, a2, a3, a4, a5, a6, a7, a8) { return fn.apply(this, arguments); };
    case 10: return function(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) { return fn.apply(this, arguments); };
    default: throw new Error("First argument to _arity must be a non-negative integer no greater than ten");
  }
}
5.整合

最后整合出來兩個最終的函數(shù)pipecompose

var pipe = (...args) => _arity(args[0].length, reduce(_pipe, args[0], tail(args)))
var remdaCompose = (...args) => pipe.apply(this, reverse(args))

再把上面的demo試一下

console.log(remdaCompose(repeat, toUpper, classyGreeting)("dong", "zhe"))
// THE NAME"S ZHE, DONG ZHETHE NAME"S ZHE, DONG ZHE

整合的完全版我放到了github里

總結(jié)

這篇文章主要分析了ramda.js實現(xiàn)compose的過程,其中分析了如何把函數(shù)看成一等公民,如何實現(xiàn)一個reduce等等。可以看出,compose的實現(xiàn)從頭到尾都是函數(shù)式編程的思想,下一篇文章打算結(jié)合社區(qū)的一道問答題來介紹一下如何用函數(shù)式思想來解決問題。我也是初學(xué)函數(shù)式,有什么說的不準(zhǔn)確的地方希望多多指正。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/81755.html

相關(guān)文章

  • JavaScript專題之函數(shù)組合

    摘要:專題系列第十六篇,講解函數(shù)組合,并且使用柯里化和函數(shù)組合實現(xiàn)模式需求我們需要寫一個函數(shù),輸入,返回。這便是函數(shù)組合。 JavaScript 專題系列第十六篇,講解函數(shù)組合,并且使用柯里化和函數(shù)組合實現(xiàn) pointfree 模式 需求 我們需要寫一個函數(shù),輸入 kevin,返回 HELLO, KEVIN。 嘗試 var toUpperCase = function(x) { return...

    周國輝 評論0 收藏0
  • 關(guān)于javascript函數(shù)式編程中compose實現(xiàn)

    摘要:結(jié)論這次主要介紹了函數(shù)式編程中的函數(shù)的原理和實現(xiàn)方法,由于篇幅原因,我把打算分析的源碼實現(xiàn)放到下一篇來介紹,可以說實現(xiàn)的更加函數(shù)式,需要單獨好好分析。 上一篇文章介紹了javascript函數(shù)式編程中curry(柯里化)的實現(xiàn),當(dāng)然那個柯里化是有限參數(shù)的柯里化,等有機會在補上無限參數(shù)的那一種柯里化,這次主要說的是javascript函數(shù)式編程中另外一個很重要的函數(shù)compose,com...

    jonh_felix 評論0 收藏0
  • Lodash 源碼分析(三)Array

    摘要:前言這是源碼分析系列文章的第三篇,前面兩篇文章源碼分析一源碼分析二分別分析了中的一些重要函數(shù),也給出了簡化的實現(xiàn),為理解其內(nèi)部機理和執(zhí)行方式提供了便利。官方也對其進行了說明。 前言 這是Lodash源碼分析系列文章的第三篇,前面兩篇文章(Lodash 源碼分析(一)Function Methods、Lodash 源碼分析(二)Function Methods)分別分析了Lodash F...

    ZoomQuiet 評論0 收藏0
  • redux源碼解讀--compose源碼解析

    摘要:源碼解析模塊的代碼十分簡練,但是實現(xiàn)的作用卻是十分強大。只傳遞一個參數(shù)的時候,就直接把這個函數(shù)返回返回組合函數(shù)這就是對源碼的一個整體解讀,水平有限,歡迎拍磚。后續(xù)的源碼解讀和測試?yán)涌梢躁P(guān)注源碼解讀倉庫 compose源碼解析 compose模塊的代碼十分簡練,但是實現(xiàn)的作用卻是十分強大。redux為何稱為redux?有人說就是reduce和flux的結(jié)合體,而reduce正是comp...

    lk20150415 評論0 收藏0
  • koa源碼解析

    摘要:用法回顧執(zhí)行順序每當(dāng)執(zhí)行時,執(zhí)行下一個中間件,執(zhí)行到最后一個中間件后開始往回執(zhí)行源碼解析源碼執(zhí)行步驟使用方法即將進中的數(shù)組中方法調(diào)用的和方法來創(chuàng)建服務(wù),的回掉執(zhí)行下面的操作回掉首先執(zhí)行方法將組合成一個對象來執(zhí)行,這個對象即可完成中 用法回顧 const Koa = require(koa); const app = new Koa(); app.use(async (ctx, nex...

    hsluoyz 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<