1. 程式人生 > >我的FP感悟

我的FP感悟

並發編程 享元 可變 結構型 理解 program tab 結果類型 而在

FP概要:

我主要總結了以下5點:

  1. 函數是一等公民: 函數的參數是函數,返回值是函數,類型還是函數...
  2. 舍棄語句,擁抱表達式: 表達式就一定有返回值
  3. 無副作用: 不修改外部閉包的狀態.
  4. immutable: 沒有變量,一切不可變,循環遞歸實現
  5. 引用透明: 程序的運行不依賴狀態,不必包.

就以上幾點,我編了一首打油詩:

函數編程很強大
一等公民都是它
外部變量不依賴
返回確保串行化

串行化什麽意思呢?
同樣計算(1+2)*3/4,java代碼如下:

int a = 1 + 2
int b = a * 3
int c = b / 4

而Scala寫出來如下:

val r = subtract(multiply(add(1, 2), 3), 4)

如此,就可以很簡單的改寫為:

val r = add(1, 2).multiply(3).subtract(4)

其中的美妙自己體會吧~~~

那麽這麽做的意義呢?

  1. 代碼簡潔
  2. 接近自然語言
  3. 函數單純,易於測試
  4. immutable,並發簡單可控
  5. 熱升級
  • 如果代碼包含了任何的var,則是指令式風格,如果全是val則是函數式風格。
  • 函數的結果類型是Unti的是有副作用的,而結果類型有明確返回類型的則是沒有副作用的。

函數式編程的歷史

有機會看到這篇文章的讀者,大概都會知道阿蘭·圖靈(Alan Turing)和約翰·馮·諾伊曼(John von Neumann)。阿蘭·圖靈提出了圖靈機的概念,約翰·馮·諾伊曼基於這一理論,設計出了第一臺現代計算機。

由於圖靈以及馮·諾伊曼式計算機的大獲成功,歷史差點淹沒了另外一位同樣傑出的科學家和他的理論,那就是阿隆佐·邱奇(Alonzo Church)和他的λ演算。

阿隆佐·邱奇是阿蘭·圖靈的老師,上世紀三十年代,他們一起在普林斯頓研究可計算性問題,為了回答這一問題,阿隆佐·邱奇提出了λ演算,其後不久,阿蘭·圖靈提出了圖靈機的概念,盡管形式不同,但後來證明,兩個理論在功能上是等價的,條條大路通羅馬。

如果不是約翰·麥卡錫(John McCarthy),阿隆佐·邱奇的λ演算恐怕還要在歷史的故紙堆中再多躺幾十年,約翰·麥卡錫是人工智能科學的奠基人之一,他發現了λ演算的珍貴價值,發明了基於λ演算的函數式編程語言:Lisp,由於其強大的表達能力,一推出就受到學術界的熱烈歡迎,以至於一段時間內,Lisp 成了人工智能領域的標準編程語言。

很快,λ演算在學術界流行開來,出現了很多函數式編程語言:Scheme 、SML、Ocaml 等,但是在工業界,還是命令式編程語言的天下,Fortran、C、C++、Java 等。

隨著時間的流逝,越來越多的計算機從業人員認識到函數式編程的意義,愛立信公司於上世紀八十年代開發出了 Erlang 語言來解決並發編程的問題;
在互聯網的發展浪潮中,越來越多的語言也開始支持函數式編程:JavaScript、Python、Ruby、Haskell、Scala 等。

可以預見,如果過去找一個懂什麽是函數式編程的程序員很困難,那麽在不久的將來,找一個一點也沒聽過函數式編程的程序員將更加困難。

面向對象編程

對於面向對象,我們已經再熟悉不過了,所以這裏不做過多介紹,直接分析它的優缺點.

面向對象編程的優點

面向對象程序設計可以看作一種在程序中包含各種獨立而又互相調用的對象的思想(抽象現實世界),這與傳統的思想剛好相反。

傳統的程序設計主張將程序看作一系列函數的集合,或者直接就是一系列對電腦下達的指令。

面向對象程序設計中的每一個對象都應該能夠接受數據、處理數據並將數據傳達給其它對象,因此它們都可以被看作一個小型的“機器”,即對象。

目前已經被證實的是,面向對象程序設計推廣了程序的靈活性和可維護性,並且在大型項目設計中廣為應用。

此外,支持者聲稱面向對象程序設計要比以往的做法更加==便於學習==(所以易於推廣),因為它能夠讓人們更簡單地設計並維護程序,使得程序更加便於分析、設計、理解。

同時它也是易拓展的,由於繼承、封裝、多態的特性,自然設計出高內聚、低耦合的系統結構,使得系統更靈活、更容易擴展,而且成本較低。

在面向對象編程的基礎上發展出來的23種設計模式廣泛應用於現今的軟件工程中,極大方便了代碼的書寫與維護。

  • 創建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
  • 結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
  • 行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、叠代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。

面向對象編程的缺點

  • (多並發問題)面向對象編程以數據為核心,所以在多線程並發編程中,多個線程同時操作數據的時候可能會導致數據修改的不確定性。
  • 在現在的軟件工程中,由於面向對象編程的濫用,導致了很多問題。
    • (冗余度大)首先就是為了寫可重用的代碼而產生了很多無用的代碼,導致代碼膨脹.
    • (難理解,難維護)同時很多人並沒有完全理解面向對象思想,為了面向對象而面向對象,使得最終的代碼晦澀難懂,給後期的維護帶來了很大的問題。
    • (大型系統不適應)所以對於大項目的開發,使用面向對象會出現一些不適應的情況。
  • (低效率)面向對象雖然開發效率高但是代碼的運行效率比起面向過程要低很多,這也限制了面向對象的使用場景不能包括那些對性能要求很苛刻的地方。

下面我們在看看函數式編程(FP)

什麽是函數式編程(FP)

狹義地說,函數式編程沒有可變的變量、循環等這些命令式編程方式中的元素,像數學裏的函數一樣,對於給定的輸入,不管你調用該函數多少次,永遠返回同樣的結果。而在我們常用的命令式編程方式中,變量用來描述事物的狀態,整個程序,不過是根據不斷變化的條件來維護這些變量。

廣義地說,函數式編程重點在函數,函數是這個世界裏的==一等公民==,函數和其他值一樣,可以到處被定義,可以作為參數傳入另一個函數,也可以作為函數的返回值,返回給調用者。利用這些特性,可以靈活組合已有函數形成新的函數,可以在更高層次上對問題進行抽象。本文的重點將放在這一部分。

函數式編程的優點

  • (不可變,易並發)在函數式編程中,由於數據全部都是不可變的,所以沒有並發編程的問題,是多線程安全的。
  • (數據一致,結果穩定)可以有效降低程序運行中所產生的副作用,對於快速叠代的項目來說,函數式編程可以實現函數與函數之間的熱切換而不用擔心數據的問題,因為它是以函數作為最小單位的,只要函數與函數之間的關系正確即可保證結果的正確性。
  • (可讀性強)函數式編程的表達方式更加符合人類日常生活中的語法,代碼可讀性更強。
    實現同樣的功能函數式編程所需要的代碼比面向對象編程要少很多,代碼更加簡潔明晰。
  • 函數式編程廣泛運用於科學研究中,因為在科研中對於代碼的工程化要求比較低,寫起來更加簡單,所以使用函數式編程開發的速度比用面向對象要高很多,如果是對開發速度要求較高但是對運行資源要求較低同時對速度要求較低的場景下使用函數式會更加高效。

函數式編程的缺點

  • (變量消耗內存)由於所有的數據都是不可變的,所以所有的變量在程序運行期間都是一直存在的,非常占用運行資源。
  • (運行速度慢)同時由於函數式的先天性設計導致性能一直不夠。雖然現代的函數式編程語言使用了很多技巧比如惰性計算等來優化運行速度,但是始終無法與面向對象的程序相比,當然面向對象程序的速度也不夠快。
  • (不普及)函數式編程雖然已經誕生了很多年,但是至今為止在工程上想要大規模使用函數式編程仍然有很多待解決的問題,尤其是對於規模比較大的工程而言。如果對函數式編程的理解不夠深刻就會導致跟面相對象一樣晦澀難懂的局面。

總結

函數式編程和面向對象編程各有利弊,一個語法更加自由(FP),一個健壯性更好(OOP)。作為程序員應該對兩種編程方式都有所了解,不管是哪種方式,只要能夠很好的解決當前的問題就是正確的方式,畢竟對於軟件工程來說解決問題是最主要的,用的工具反而沒有那麽重要,就像對程序員來說語言不重要,重要的是解決問題的思想。

現在這兩者的發展趨勢是相互借鑒的,許多以面向對象作為基礎的語言例如Java等都在新的版本中添加了對函數式編程的支持,而函數式編程則借鑒了一些在面向對象語言裏用的一些編譯技巧使得程序運行更快。

細說FP的優點

約翰·巴克斯(John Backus)為人熟知的兩項成就是 FORTRAN 語言和用於描述形式系統的巴克斯範式,因為這兩項成就,他獲得了 1977 年的圖靈獎。

有趣的是他在獲獎後,做了一個關於函數式編程的講演:Can Programming Be Liberated From the von Neumann Style? 1977 Turing Award Lecture。他認為像 FORTRAN 這樣的命令式語言不夠高級,應該有新的,更高級的語言可以擺脫馮諾依曼模型的限制,於是他又發明了 FP 語言,雖然這個語言未獲成功,但是約翰·巴克斯關於函數式編程的論述卻得到了越來越多的認可。下面,我們就羅列一些函數式編程的優點。

首先,函數式編程天然有並發的優勢。由於工藝限制,摩爾定律已經失效,芯片廠商只能采取多核策略。程序要利用多核運算,必須采取並發,而並發最頭疼的問題就是共享數據,狹義的函數式編程沒有可變的變量,數據只讀不寫,並發的問題迎刃而解。這也是前面兩篇文章中,一直建議大家在定義變量時,使用 val 而不是 var 的原因。愛立信公司發明的 Erlang 語言就是為解決並發的問題而生,在電信行業取得了不俗的成績。

其次,函數式編程有跡可尋。由於不依賴外部變量,給定輸入函數的返回結果永遠不變,對於復雜的程序,我們可以用值替換的方式(substitution model)化繁為簡,輕松得出一段程序的計算結果。為這樣的程序寫單元測試也很方便,因為不用擔心環境的影響。

最後,函數式編程高屋建瓴。寫程序最重要的就是抽象,不同風格的編程語言為我們提供了不同的抽象層次,抽象層次越高,表達問題越簡潔,越優雅。讀者從下面的例子中可以看到,使用函數式編程,有一種高屋建瓴的感覺。

我的FP感悟