1. 程式人生 > >Scala 函數語言程式設計

Scala 函數語言程式設計

a、什麼是函數語言程式設計

函數語言程式設計是一種程式設計正規化(即編寫程式的方法論),它主要通過組合方式,將運算過程組合成一系列的函式呼叫。如下的數學表示式:

(1 + 2) * 3 - 4
傳統的程序式程式設計,可能這樣寫:
var a = 1 + 2
var b = a * 3
var c = b - 4
而函式程式設計則就要將每個運算過程定義為不同的函式,然後組合起來使用,返回最終的計算結果。如下
var result = subtract(multiply(add(1,2), 3), 4);
這就是函數語言程式設計。

b、函數語言程式設計和麵向物件程式設計的區別

函數語言程式設計和麵向物件程式設計是兩種不同的看待問題的方式,函數語言程式設計強調的是“動詞(verbs)”以及如何組合和呼叫它們。而面向物件程式設計強調的是”名詞(nouns)“以及它們所具有的動作。

面向物件程式設計中,每個物件都有識別符號、方法、狀態(成員變數)。在標識物件、物件狀態、行為後,物件之間的互動就成了問題,因為互動需要在一個物件裡完成。現在常用的方法是建立一個服務類(Service Class),它包含一個跨多個域物件的操作集合。服務類也是一個物件,它沒有狀態或行為獨立於其操作物件的概念。

函數語言程式設計側重於函式的組合和應用,所有的變數都是不可變的,並且儘可能的消除副作用,這些都有利於併發程式設計。

舉個例子,分別採用面向物件程式設計和函數語言程式設計的方式,對下面的一句話程式設計:

一隻貓捕捉了一隻鳥,並吃了它。
採用面向物件程式設計的方式,可以從這句話中提取出兩個名詞:貓、鳥,並且貓具有“捕捉”和“吃”的動作。程式碼如下:
class Bird
class Cat {
  def catch(b: Bird): Unit = ...
  def eat(): Unit = ...
}
val cat = new Cat
val bird = new Bird
cat.catch(bird)
cat.eat()
採用函式是程式設計的方式,則應關注“動詞”
trait Cat
trait Bird
trait Catch
trait FullTummy

def catch(hunter: Cat, prey: Bird): Cat with Catch
def eat(consumer: Cat with Catch): Cat with FullTummy

val story = (catch _) andThen (eat _)
story(new Cat, new Bird)

c、函數語言程式設計的特點

1、函式是“第一等公民”

所謂的“第一等公民”是指,函式和其它的資料型別一樣,處於平等地位,它可以給變數賦值,可以作為引數傳入另一個函式,也可以作為別的函式的返回值。

2、只用表示式,不用語句

“表示式”指的是一個單純的運算過程,總有返回值;而“語句”是執行某種操作,沒有返回值。函數語言程式設計要求,只使用表示式,不使用語句。也就是說,每一步都是單純的運算,而且都有返回值。

3、沒有“副作用”

所謂的“副作用”是指,函式內部與外部的互動,不會改變外部的狀態,例如修改外部的成員變數或修改全域性變數的值等。

4、引用透明性

引用透明性是指函式的執行不依賴與外部的變數或狀態,只依賴與輸入的引數,只要輸入的引數相同,輸入的結果始終是相同的。

d、函數語言程式設計的優點

1、程式碼簡潔,開發快速

函數語言程式設計大量使用函式,減少了程式碼的重複,因此程式比較短,開發速度快。

2、接近自然語言,易於理解

函數語言程式設計的自由度很高,可以寫出很接近自然語言的程式碼。

前文曾經將表示式(1 + 2) * 3 - 4,寫成函式式語言:

subtract(multiply(add(1,2), 3), 4)
對它進行變形,不難得到另一種寫法:
add(1,2).multiply(3).subtract(4)
這基本就是自然語言的表達了。再看下面的程式碼,大家應該一眼就能明白它的意思吧:
merge([1,2],[3,4]).sort().search("2")

因此,函數語言程式設計的程式碼更容易理解。

3、更方便的程式碼管理

函數語言程式設計不依賴、也不會改變外界的狀態,只要給定輸入引數,返回的結果必定相同。因此,每一個函式都可以被看做獨立單元,很有利於進行單元測試(unit testing)和除錯(debugging),以及模組化組合。

4、易於“併發程式設計”

函數語言程式設計不需要考慮"死鎖"(deadlock),因為它不修改變數,所以根本不存在"鎖"執行緒的問題。不必擔心一個執行緒的資料,被另一個執行緒修改,所以可以很放心地把工作分攤到多個執行緒,部署"併發程式設計"(concurrency)。

請看下面的程式碼:

  var s1 = Op1();
  var s2 = Op2();
  var s3 = concat(s1, s2);
由於s1和s2互不干擾,不會修改變數,誰先執行是無所謂的,所以可以放心地增加執行緒,把它們分配在兩個執行緒上完成。其他型別的語言就做不到這一點,因為s1可能會修改系統狀態,而s2可能會用到這些狀態,所以必須保證s2在s1之後執行,自然也就不能部署到其他執行緒上了。

多核CPU是將來的潮流,所以函數語言程式設計的這個特性非常重要。

5、程式碼的熱升級

函數語言程式設計沒有副作用,只要保證介面不變,內部實現是外部無關的。所以,可以在執行狀態下直接升級程式碼,不需要重啟,也不需要停機。Erlang語言早就證明了這一點,它是瑞典愛立信公司為了管理電話系統而開發的,電話系統的升級當然是不能停機的。