版權(quán)所有 ? 2018 林鵬程, 保留所有權(quán)利。
最近,在微信群里介紹純函數(shù)管道數(shù)據(jù)流時(shí),回群友關(guān)于構(gòu)造方法的提問(wèn),整理一下,放這里。
純函數(shù)管道數(shù)據(jù)流是我在個(gè)人項(xiàng)目實(shí)踐過(guò)程總結(jié)創(chuàng)造出來(lái)的方法,基于我的知識(shí)體系,融合了集成電路、科學(xué)化管理思想、大工業(yè)流水線(xiàn)思想、函數(shù)式編程(FP)、統(tǒng)計(jì)學(xué)R語(yǔ)言的向量式編程等技術(shù),在系統(tǒng)構(gòu)造過(guò)程中的抽象、策略選擇判斷過(guò)程應(yīng)用了科學(xué)化管理思想、大工業(yè)流水線(xiàn)思想來(lái)做為依據(jù),是最契合我的思維方式的方法,不是象那些FP和OO狂信徒從外來(lái)的理論學(xué)習(xí)、沉迷、洗腦、走火入魔的狀態(tài):-)。
大海航行靠舵手,編程朝著數(shù)據(jù)走。初始狀態(tài),最終狀態(tài),兩點(diǎn)間直線(xiàn)距離最短。 沿著數(shù)據(jù)變換形成的數(shù)據(jù)流,沿途匯流,直至最終狀態(tài),這叫百川東到海、結(jié)果導(dǎo)向、直指大道, 這種做法與波音飛機(jī)“脈動(dòng)”生產(chǎn)線(xiàn)做法是一致的。天下武功,唯快不破。 簡(jiǎn)單直接是快速穩(wěn)定可靠的根本。FP和OO其實(shí)全是在走彎路 :-)。
純函數(shù)管道數(shù)據(jù)流方法是使項(xiàng)目純粹由->>塊組成,一個(gè)->>塊函數(shù)相當(dāng)于一個(gè)集成電路元件(或板),這樣整個(gè)項(xiàng)目象個(gè)集成電路(或城市水網(wǎng),或企業(yè)制度流程)。代碼示意圖化和象形化 對(duì)系統(tǒng)架構(gòu)邏輯和流程類(lèi)似所見(jiàn)即所得,對(duì)閱讀、理解、優(yōu)化很有幫助。每一步都是可測(cè)試、可驗(yàn)證,代碼重用和組合非常的爽,邏輯也很簡(jiǎn)潔清晰,系統(tǒng)構(gòu)造、調(diào)試、擴(kuò)展、維護(hù)、使用也可以引用成熟的集成電路理論。
數(shù)據(jù)流設(shè)計(jì)時(shí),要特別注意設(shè)立函數(shù)輸入輸出的數(shù)據(jù)規(guī)范標(biāo)準(zhǔn),按大工業(yè)標(biāo)準(zhǔn)化生產(chǎn)模式。
因?yàn)?code>Clojure語(yǔ)言對(duì)純函數(shù)管道數(shù)據(jù)流提供了語(yǔ)言級(jí)核心支持,有很多種類(lèi)的->>宏,寫(xiě)純函數(shù)管道數(shù)據(jù)流最自然最方便,所以以下內(nèi)容的代碼用Clojure語(yǔ)言表達(dá)。
1.元件:一個(gè)->>塊函數(shù)相當(dāng)于一個(gè)集成電路元件(或板),利用純函數(shù)的輸入輸出特性當(dāng)作管道(導(dǎo)線(xiàn))使用。
一個(gè)->>塊里面的一系列函數(shù),最多只能有一個(gè)帶副作用的函數(shù)且只能處于末尾。另外,要注意做好數(shù)據(jù)標(biāo)準(zhǔn)化工作,在出入口檢查,中間就可以極限裸奔,這樣做簡(jiǎn)潔、流暢、穩(wěn)定、高效。
一般只有在帶副作用的函數(shù)里才有try-catch異常處理,異常處理是個(gè)垃圾機(jī)制,我不喜歡try-catch異常機(jī)制,使用try-catch異常機(jī)制也只是為了兼容系統(tǒng)外部的庫(kù)、語(yǔ)言和平臺(tái)的異常機(jī)制而已,對(duì)外來(lái)的只能接受,只能管好自己。在數(shù)據(jù)流里,一切用數(shù)據(jù)解決,可以接著向下傳遞數(shù)據(jù)信號(hào)表達(dá)狀況,這種思想類(lèi)似C語(yǔ)言的錯(cuò)誤號(hào)數(shù)據(jù)傳遞機(jī)制。經(jīng)典的計(jì)算機(jī)技術(shù)很多是很好的,比如數(shù)據(jù)結(jié)構(gòu)+算法,錯(cuò)誤號(hào)等。一幫不知所謂的家伙炒作出FP/OO/異常等一堆垃圾,為創(chuàng)新而創(chuàng)新,為paper而創(chuàng)新。
在clojure語(yǔ)言里,建議函數(shù)盡量設(shè)計(jì)成參數(shù)為hash-map類(lèi)型的單參數(shù)函數(shù),象R語(yǔ)言大多數(shù)函數(shù)那樣,可以設(shè)計(jì)很多帶默認(rèn)值的命名參數(shù),有很強(qiáng)的可擴(kuò)展性。另外,clojure操作hash-map的核心函數(shù)很多,操作方便,不僅在使用->>宏時(shí)可以不用寫(xiě)括號(hào),而且參數(shù)的形成,校驗(yàn),變換與函數(shù)調(diào)用一體化、一條龍數(shù)據(jù)流處理。還有clojure解構(gòu)方便,在函數(shù)體內(nèi)形式參數(shù)使用上與一般多參數(shù)函數(shù)是一樣方便的。
(defn f [x]
(->> x
f1
f2))
(defn f [{:keys [x y] :as m}]
(->> x
(f1 y ,)
f2))
2.分支:一個(gè)(cond)或(if)塊作為一個(gè)函數(shù)。代碼示意:
(defn f [x]
(cond
(= x 1) (f1)
(= x 2) (f2)
:else (f3)))
(defn f2 [x y]
(-> (> x 2)
(and , (< y 6))
(if , 25 30)))
(defn path-combine [s1 s2]
(cond
(string/starts-with? s2 "/")
s2
(not (string/ends-with? s1 "/"))
(-> (string/split s1 #"[\\/]")
butlast
(#(string/join "/" %))
(str , "/")
(path-combine , s2))
:else
(-> (string/join "/" [s1 s2])
(string/replace , #"[\\/]+" "/"))))
3.反饋電路(回流):一個(gè)尾遞歸函數(shù)相當(dāng)于一個(gè)反饋電路。備注:map是批處理,另外也可以看成類(lèi)似對(duì)一個(gè)游客隊(duì)列,在入口重復(fù)進(jìn)行驗(yàn)票動(dòng)作,是一個(gè)前進(jìn)動(dòng)作,不是反饋或回流。
(defn f [i]
(if-not (zero? i)
(f1)
(-> i dec recur)))
4.分流:相當(dāng)于并發(fā)或并行。例如:對(duì)數(shù)據(jù)進(jìn)行分塊,并行處理,代碼示意:
(->> data
(partition n ,)
(pmap f ,))
(->> [pipe-f1 pipe-f2 pipe-f3]
(pmap #(% data) ,))
5.合流:相當(dāng)對(duì)分流的結(jié)果進(jìn)行reduce,聯(lián)合后的代碼示意:
(->> data
(partition n ,)
(pmap f1 ,)
(reduce f2 ,))
注1:和其他數(shù)據(jù)流的區(qū)別:
以前也曾出現(xiàn)很多數(shù)據(jù)流思想,但實(shí)現(xiàn)和設(shè)計(jì)都很不理想,從代碼上看與集成電路相差極遠(yuǎn)。純函數(shù)管道數(shù)據(jù)流clojure最合適,從語(yǔ)義和表現(xiàn)形式上看,都是最簡(jiǎn)單直觀的。下面附帶一個(gè)代碼比較鏈接:
1.我的數(shù)據(jù)流代碼大概是這個(gè)形式:
https://clojureverse.org/t/how-to-join-file-paths/814/12
一般是一個(gè)->>塊一個(gè)函數(shù)。
2.對(duì)比一下akka的數(shù)據(jù)流:
https://doc.akka.io/docs/akka/current/stream/stream-composition.html
我們可以看出它們代碼的象形和邏輯上的差距,akka代碼與文章里的集成電路示意圖形式上差距很大。
更不用說(shuō)Rxjava那種丑爆的代碼
注2:與ring middleware區(qū)別
代碼看起來(lái)相似,但理念是本質(zhì)上的差別。
1.middleware函數(shù)的輸入輸出是函數(shù),流轉(zhuǎn)的是層層打包的函數(shù),是同心圓,middleware調(diào)試是很麻煩的。
2.純函數(shù)管道數(shù)據(jù)流輸入輸出是數(shù)據(jù),流轉(zhuǎn)的是數(shù)據(jù),是串并聯(lián)。
3.我不能認(rèn)同中間件的理念,和集成電路思想是沖突的。電路里,元件(電路板)是無(wú)法流轉(zhuǎn)的,只有數(shù)據(jù)(電流)才能流轉(zhuǎn),理解不要有偏差。
注3:本文主講系統(tǒng)構(gòu)造,不在于精妙的代碼,文章內(nèi)是偽代碼,整個(gè)軟件工程100%由純粹的->>塊構(gòu)成才是重點(diǎn),也是難點(diǎn), ->>宏使用這種語(yǔ)法不是重點(diǎn),->>構(gòu)造系統(tǒng)是一種流程優(yōu)化,使數(shù)據(jù)和邏輯分離得很清楚,達(dá)到科學(xué)、簡(jiǎn)單、靈活的架構(gòu)。不用->>架構(gòu)的架構(gòu)數(shù)據(jù)和邏輯混雜,我稱(chēng)為“可怕的泥石流”。
最后,大道至簡(jiǎn),這種系統(tǒng)構(gòu)造方法看起來(lái)方法很簡(jiǎn)單,象自由博擊和形意拳一樣,只有基本元件和基本組合方法,需要把基本功練成本能,神而明之、觸之即發(fā)、千變?nèi)f化的應(yīng)用,不要用固定的模式限制自己的想象力,沒(méi)有FP和OO那么多套路和招數(shù),但是對(duì)數(shù)據(jù)抽象和邏輯抽象的要求比FP還要高得多,輔之道法自然、千變?nèi)f化、天馬行空的想像力和創(chuàng)造力。
做一件好事很容易,一輩子純做好事很難。寫(xiě)一個(gè)純函數(shù)管道數(shù)據(jù)流函數(shù)很容易,寫(xiě)一個(gè)100%由純函數(shù)管道數(shù)據(jù)流函數(shù)組成的系統(tǒng)很難,用最簡(jiǎn)單的方法把一個(gè)復(fù)雜系統(tǒng)構(gòu)造得簡(jiǎn)潔清楚,使之看起來(lái)好象很簡(jiǎn)單,這已經(jīng)是大師級(jí)以上的水準(zhǔn)了。
看到能把FP用來(lái)工作的人數(shù)那么少,我是不指望本方法能流行,但發(fā)表出來(lái),如果能幫助他人開(kāi)拓思想,有所得益,還是一件令人快樂(lè)的事。