摘要:至此,簡化版的就完成了。可以看出,的實現從頭到尾都是函數式編程的思想,下一篇文章打算結合社區的一道問答題來介紹一下如何用函數式思想來解決問題。我也是初學函數式,有什么說的不準確的地方希望多多指正。
前言
上一篇文章介紹了javascript中的compose函數的實現,我是用了遞歸的思想去讓函數依次執行,lodash中是用了迭代的思想依次執行函數,但實現了以后我還是覺得有些別扭,仔細想想,我們實現的是一個函數式編程用到的函數,但是實現的方法還是太命令式了,函數還是命令式的執行,通俗點說,還是太把函數當成函數了,在我的理解中,函數和普通變量沒什么區別,只是執行的方法不一樣,一旦賦予了函數這個執行的屬性,我們就可以完全將函數當成普通變量去對待。
實現 1.函數世界的加號函數和普通變量沒什么區別,只是需要偶爾執行一下
舉個例子
1 + 2 = 3 "a" + "b" = "ab" func1 "+" func2 -> func3
前兩個例子就是普通變量的操作,最后一個例子是函數的操作,本質上看來,沒有任何區別,兩個函數作用的結果就是生成一個函數,只不過在函數的世界里,這個加號的意義就是如何變換生成一個新的函數,回到compose來,在compose中,加號的意義就是把一個函數的執行結果當成下一個函數的輸入,最后在生成一個函數,就像下面這樣
var fn = (func1, func2) => (...args) => func2.call(this, func1.apply(this, args))
在這個例子里面,func1的執行結果就是func2的參數,并且生成了一個新的函數fn,我們給這個fn傳遞參數,它就會作為func1的參數來啟動執行,最后得到了函數依次執行的效果,這就是最簡單的compose,這個函數就是ramda.js實現compsoe需要的第一個函數_pipe
var _pipe = (f, g) => (...args) => g.call(this, f.apply(this, args))
_pipe就定義了compose中所謂加號的意義了。
2."不一樣的"reduce在這里提到了reduce,是不是有一點感覺,reduce的作用就是讓一個數組不斷的執行下去,所以肯定能和咱們這個compose有點聯系,先舉個reduce最常用的例子,求數組的和
var a = [1,2,3,4,5] a.reduce((x, y) => x + y, 0)
這個就是不斷的將兩個數求和,生成一個新的數,再去和下一個數求和,最后得到15,下面想一下,如果把數字換成函數會怎么樣,兩個函數結合生成一個新的函數,這個結合法則就使用上面的_pipe,這個新的函數再去結合下一個函數,直到最后一個函數執行完,我們得到的還是函數,我們前面說了,函數知識偶爾需要執行一下,這個函數的生成和執行過程是反向遞歸的過程。利用這個思想,就可以寥寥幾行(甚至只需要一行)就寫出來這個非常函數式的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())
舉個例子驗證一下,我們把首個函數做多元處理,再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
我在這里把函數生成過程分析一下
首先我們用_pipe組合classyGreeting,toUpper
f1 = _pipe(classyGreeting, toUpper) f1 = (...args) => toUpper.call(this, classyGreeting.apply(this, args))
_pipe繼續結合f1, repeat
f2 = _pipe(f1, repeat) f2 = (...args) => repeat.call(this, f1.apply(this, args))
函數的執行過程就會將參數層層傳遞到最里面的classyGreeting開始執行,從而完成函數的依次執行。ramda.js自己實現了reduce,不僅支持數組的reduce,還支持多種數據結構的reduce,(兼容性也更好?),下一步來分析是如何自己實現數組的reduce的,可與看出,自己分離出來邏輯之后,函數的執行過程和組合的規則部分將分離的更徹底。
3.自己寫一個reducereduce接受三個參數,執行函數,初始值,執行隊列(可以不止為一個數組),返回一個針對這些參數的reduce處理,這里只寫數組部分(_arrayReduce),源碼中還包含了關于迭代器的_iterableReduce 等等,而且ramda.js對執行函數也有一層對象封裝,擴展了函數的功能
var reduce = (fn, acc, list) => (fn = _xwrap(fn), _arrayReduce(fn, acc, list))
在寫_arrayReduce之前,先來看一下函數的對象封裝_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); }; })()
其實就是對函數執行狀態做了一個分類管理
@@transducer/step 這種狀態認為是一種過程狀態
@@transducer/result 這種狀態被認為是一種結果狀態
這種狀態管理通過對象也是合情合理的
最后再來完成_arrayReduce,就很簡單了,這個函數只是專心一件事情,就是寫reduce的過程規則。
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用來分離初始值和執行隊列的,因為初始函數是多元的(接收多個參數),執行隊列都是一元(接收一個參數)的,分離還是有必要的
var tail = arr => arr.slice(1)
reverse改變執行順序
var reverse = arr => arr.reverse()
_arity我把源代碼貼出來,我也不知道為什么這樣做,可能是明確指定參數吧,因為reduce生成的函數是可以接受多個參數的,_arity就是處理這個函數的
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.整合
最后整合出來兩個最終的函數pipe和compose
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里
總結這篇文章主要分析了ramda.js實現compose的過程,其中分析了如何把函數看成一等公民,如何實現一個reduce等等。可以看出,compose的實現從頭到尾都是函數式編程的思想,下一篇文章打算結合社區的一道問答題來介紹一下如何用函數式思想來解決問題。我也是初學函數式,有什么說的不準確的地方希望多多指正。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81755.html
摘要:專題系列第十六篇,講解函數組合,并且使用柯里化和函數組合實現模式需求我們需要寫一個函數,輸入,返回。這便是函數組合。 JavaScript 專題系列第十六篇,講解函數組合,并且使用柯里化和函數組合實現 pointfree 模式 需求 我們需要寫一個函數,輸入 kevin,返回 HELLO, KEVIN。 嘗試 var toUpperCase = function(x) { return...
摘要:結論這次主要介紹了函數式編程中的函數的原理和實現方法,由于篇幅原因,我把打算分析的源碼實現放到下一篇來介紹,可以說實現的更加函數式,需要單獨好好分析。 上一篇文章介紹了javascript函數式編程中curry(柯里化)的實現,當然那個柯里化是有限參數的柯里化,等有機會在補上無限參數的那一種柯里化,這次主要說的是javascript函數式編程中另外一個很重要的函數compose,com...
摘要:前言這是源碼分析系列文章的第三篇,前面兩篇文章源碼分析一源碼分析二分別分析了中的一些重要函數,也給出了簡化的實現,為理解其內部機理和執行方式提供了便利。官方也對其進行了說明。 前言 這是Lodash源碼分析系列文章的第三篇,前面兩篇文章(Lodash 源碼分析(一)Function Methods、Lodash 源碼分析(二)Function Methods)分別分析了Lodash F...
摘要:源碼解析模塊的代碼十分簡練,但是實現的作用卻是十分強大。只傳遞一個參數的時候,就直接把這個函數返回返回組合函數這就是對源碼的一個整體解讀,水平有限,歡迎拍磚。后續的源碼解讀和測試例子可以關注源碼解讀倉庫 compose源碼解析 compose模塊的代碼十分簡練,但是實現的作用卻是十分強大。redux為何稱為redux?有人說就是reduce和flux的結合體,而reduce正是comp...
閱讀 3735·2021-11-24 10:46
閱讀 1706·2021-11-15 11:38
閱讀 3761·2021-11-15 11:37
閱讀 3481·2021-10-27 14:19
閱讀 1939·2021-09-03 10:36
閱讀 1991·2021-08-16 11:02
閱讀 2998·2019-08-30 15:55
閱讀 2251·2019-08-30 15:44