程序的本質是什麼?數據結構+算法!!!我想這也是不少程序員給出的答案,我本身也承認這一觀點,當咱們瞭解了某一門編程語以後,接下來咱們面對的每每是數據結構和算法的學習。而如今,我對於程序的本質有了不同的答案:分化和組合。個人老師曾經告訴我,工程師或者程序員有一個很重要的能力(我猜這也是最重要的能力),那就是發現和解決問題。<!-- More -->而我目前只能接觸到解決問題的層次,這也是我也一直在探尋的目標 —— 分化和組合。程序員
在現實的程序開發中,咱們碰到的問題通常是一個很大的命題,難以抽象,這時候每每經過分化子問題的方式對程序進行分而治之。在算法裏,這種方式叫作分治。算法
將程序分化成能夠抽象的子程序以後,再將他們組合成一個具備完備功能的程序。組合是程序或代碼可複用的前提,函數式編程中組合的使用也會給咱們帶來意向不到的驚喜編程
Haskell是純函數式編程語言,他的強大不用我多說,這裏展現一下他的組合能力。數組
compose :: (b -> c) -> (a -> b) -> a -> c compose f g = f . g
在函數式編程的組合中,咱們是從右到左執行的,上述的例子中咱們藉助 (.) 函數實現組合,固然,咱們也能夠用本身的方式實現。數據結構
compose :: (b -> c) -> (a -> b) -> a -> c compose f g x = f (g x)
經過 Haskell 強大的類型簽名中,大體能夠推出compose函數的做用。b -> c
表明了一個從b
類到c
類的映射函數,a -> b
也是同樣,compose
函數接受兩個函數f
和g
,返回一個新的函數h
,h
函數表達了從a
類到b
類再到c
類的映射。數據結構和算法
舉個例子,咱們有sum
函數 —— 給列表求和,odd
函數 —— 判斷數字是不是奇數。(簡單起見,給他們的類型簽名並不許確,但足夠簡單)編程語言
odd :: Int -> Bool sum :: [Int] -> Int sumIsOdd :: [Int] -> Bool sumIsOdd = compose odd sum sumIsOdd [1,2,34,5]
sumIsOdd
函數組合了求和與判斷奇數兩個函數,他並沒實現具體的功能,而是經過一種通俗的組合實現了將單一的函數轉化成稍微複雜的函數,從而達到功能上的擴充,我想這就是組合的魅力,也是函數式的魅力之一吧。函數式編程
JavaScript是一門表現力極強的語言,除了本質上對面向對象的支持,對函數式編程的支持也絲絕不弱,組合函數實現起來雖然複雜,可是也未必不可行,並且相對於Haskell的晦澀,JavaScript更加簡單一點函數
const odd = x => x % 2 !== 0; const sum = args => args.reduce((a, b) => a + b); const compose = (...fs) => arg => fs.reduceRight((f, g) => g.call(null, f), arg);
藉助ES6的箭頭函數實現起來駕輕就熟,可見即使是JavaScript這種以面向對象思想設計的語言也但願在函數式編程上面能有所建樹。學習
順帶一提,對數組擴充的map
、filter
、reduce
等函數,也是JavaScript對函數式編程的支持,雖然只是利用while循環作的語法糖,效率也不如while循環,可是在JavaScript的語境中,不多對效率有極高的要求,因此從語義上來說,我更傾向用函數式的方式解決純數據的問題。
函數式編程隨着多核CPU的發展,開始再次出如今咱們的視野中,有時候也會擔憂過於吹捧函數式,反而落入俗套。仔細想一想,編程範式並非一枝獨秀的世界,而是百家爭鳴的,各有擅長的領域。固然在一些環境下二者也是可以共存的,甚至二者同時帶來的收益可能更加可觀呢。
說回組合,其實我本身也只是一個半吊子,也須要不斷的學習才能對組合有一個更加清晰的認識,而不是拘泥於語言,在實際生產中分化和組合也實在過重要了,並非一兩個函數可以歸納的,如今的也我只能以這種簡單的方式做了解了。