函數式編程有必要學設計模式和演算法嗎?

它是否完全有一套自己的邏輯?

目前編程領域的風向變了嗎?react,redux的流行,面向對象的統治力是否在慢慢削弱?

新手應該如何抉擇?


沒有必要學設計模式

可以學演算法


要不要學設計模式?

來,我們先看下什麼是設計模式:

上面這段話來自於

為什麼不看看函數式編程相關的設計模式呢?

念念不忘,必有迴響。


https://www.cs.cmu.edu/~rwh/theses/okasaki.pdf

你應該看得是這個。在進行純粹的函數式編程的時候,你以前學的設計模式和ACM類演算法都沒有任何鳥用。就連複雜度怎麼算,都要重新學習一遍。

當然了,很少有人能使用這種技術來找到一個普通的碼農的工作(逃

====

P.S. 有些人覺得可變數據結構的演算法也能直接FP維持相同的時間/空間複雜度,還是需要再學習一個。


你看看C為什麼不走Java這條路,

Go也不走Java這條路

Ken 這個老怪物難道不是外星人?


1. react跟函數式沒啥聯繫,其實丫是個命令式+oop。redux倒是可以折騰成函數式表現,然而你還是可以命令式擼到底。真要想用純函數式擼前端,你得考慮clojure/cljs

2. 針對某人的答案,數據結構和演算法甭管你是不是ACM學的,本質一點都不變。演算法和數據結構都是可以脫離語言和範式的存在,背代碼式的ACMer註定不是好ACMer,難不成FP的快排就不是快排了?FP里表示棵樹就不是樹了?FP里就不接圖論了?

3. 函數式不是啥新鮮玩意,哪怕你用C++,如今都能體驗一把不那麼純正但是思想基本一致的函數式。是好是壞不好說,但函數式的主要優勢在於改為並發的玩意基本可以不用考慮很多問題,缺點主要在於與主流習慣的不一致性,許多的寫法用法習慣需要改變。暫時並沒發現什麼必要性,但是其中的思考方式天生適合描述演算法以及並發,前端應用真不多,計算啊後端啊這些方向上倒是會有必要多看看多了解。

===========================

說各種sort一類無法用函數式實現的,對於不可變數的列表的排序,產生額外附加的列表無可厚非。關於Quicksort對FP中不是最佳排序實現的問題,不正是因為先有演算法解析才有的如此結論?不過儘管做inplace的quicksort有諸多難點,但是merge sort確是完善的,或者說更適用於FP的設計。

儘管如此,對於需要一個以quicksort方向思考的計算,且拋去in place這一點,可以寫成如下

qsort xs = qsort" xs []

qsort" [] r = r
qsort" [x] r = x:r
qsort" (x:xs) r = qpart xs [] [] r where
qpart [] as bs r = qsort" as (x:qsort" bs r)
qpart (x":xs") as bs r | x" &<= x = qpart xs" (x":as) bs r | x" &> x = qpart xs" as (x":bs) r

嗯因為不能用++這種線性時間的玩意了嘛。相對的,merge sort這種玩意本就不是in place的,那麼

sort = sortBy compare
sortBy cmp = mergeAll . sequences
where
sequences (a:b:xs)
| a `cmp` b == GT = descending b [a] xs
| otherwise = ascending b (a:) xs
sequences xs = [xs]

descending a as (b:bs)
| a `cmp` b == GT = descending b (a:as) bs
descending a as bs = (a:as): sequences bs

ascending a as (b:bs)
| a `cmp` b /= GT = ascending b (ys -&> as (a:ys)) bs
ascending a as bs = as [a]: sequences bs

mergeAll [x] = x
mergeAll xs = mergeAll (mergePairs xs)

mergePairs (a:b:xs) = merge a b: mergePairs xs
mergePairs xs = xs

merge as@(a:as") bs@(b:bs")
| a `cmp` b == GT = b:merge as bs"
| otherwise = a:merge as" bs
merge [] bs = bs
merge as [] = as

這是GHC的庫里的sort,也是個merge sort,演算法上的一致性有保障了吧?嗯……要簡單一點的版本當然不如這個通用,但是也還不賴。這個實現是自底向上的。下面是個簡化版,比庫里的差一點,看起來很清楚。

mergePairs (sorted1 : sorted2 : sorteds) = merge sorted1 sorted2 : mergePairs sorteds
mergePairs sorteds = sorteds

mergeSortBottomUp list = mergeAll (map (x -&> [x]) list)

mergeAll [sorted] = sorted
mergeAll sorteds = mergeAll (mergePairs sorteds)

又或者必須要個自頂向下的?

mergesort :: Ord a =&> [a] -&> [a]
mergesort [] = []
mergesort [a] = [a]
mergesort l = merge (mergesort as) (mergesort bs)
where halve [] = ([], [])
halve [x] = ([x], [])
halve (x:y:xys) = (x:xs, y:ys)
where (xs, ys) = halve(xys)
(as, bs) = halve l
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys) | x &< y = x:merge xs (y:ys) | otherwise = y:merge (x:xs) ys

這是不能實現么?還是說覺得這個merge sort不是個O(nlogn)?


學學沒什麼壞處,當side effect就是需要model的因素之一的時候,FP會比較尷尬,比如model時間這個因素。而且OO不僅可以當coding的指導思想,也可以作為非常high level的系統劃分的指導思想。作為high level的communication的語言和manager,customer,PM交流。你跟他們講你發明了一種新的monad那就是作死。

學習FP可以幫助你從本質上思考問題,發現/發明 新的OO設計模式。

OO的自上而下,加上FP的自下而上

隨心所欲,威力無窮。


正好前些天聊過這個話題。面向對象接近人認知習慣,命令式反映機器特徵,函數式源於實現數學公式推導的需求,反映程序本身是嚴密的數學邏輯這一本質,這三者並不互相排斥,都學一點,在合適的地方用方便的範式才是最好的。

響應式則是簡化連鎖反應的手法,雖然現在流行 FRP,它跟函數式只是配合自然,並不是一回事兒,比如早年 PowerBuilder 的 DataWindow,Delphi 的 DataSet Components,QT的 Signal/Slot 以及Maya(DGNode)、Houdini(*OP)等三維圖形軟體包都很好的實踐了響應式,實際上 FRP 這個詞就是做三維圖形實現研究過程催生出來的。

比如,Redux 就是對 MVC 細化和發展,集中式的狀態 Store 就是模型嘛,引入了函數式的狀態歸約,讓模型(狀態)的管理更加清楚,Undo/Redo 也非常輕鬆,但是無副作用的鏈式歸約相對於副作用的狀態修改開銷要大就是代價嘍,在實際項目里總是要考慮內存和CPU用量的。

又比如,函數式編程也要搞能構成狀態作用域的閉包,這與面象對象的屬性+方法在思路上並無二致。


推薦閱讀:

請問用c語言怎麼寫出原始的除法運算的函數?就是在不用到"/"的情況下
你做過的最有效的提高你的編程水平的一件事情是什麼?對於那些想提高水平的程序員,你的建議是什麼?
自己寫的程序被殺毒軟體殺了怎麼辦?
體積小的軟體佔用內存就小嗎?
在Linux下,如何強制讓GCC靜態鏈接?

TAG:JavaScript | 編程語言 | 編程 | 函數式編程 | 設計模式 |