阿袁中午和阿靜一塊兒吃午飯。阿袁提及他最近看的《艾倫·圖靈傳 如謎的解謎者》。
因爲阿袁最近在學習Scala,因此關注了一下圖靈傳中關於函數式編程的一些歷史。
關於函數式編程的故事,能夠從1928年開始講起:希爾伯特在當年的一個大會上,提出了他的問題:程序員
希爾伯特的哲學企圖是:每一個問題的答案都將會是「是」。我想這個信念來自於對數學的神聖信仰。算法
不幸的是,在這同一個大會上,第一個問題就被否認了。一個年輕的捷克數學家,柯特·哥德爾的宣佈,可以證實,
算術必定是不完備的:存在既不能證實,也不能證僞的命題。express
注:歐幾里得幾何的五大公理並非一個反例。歐幾里得幾何能夠被一階公理化爲一個完備的系統。
(這句話啥意思?)個人理解是:公理是一個定義,或者說是不證自明的。編程
隨後,哥德爾不完備定理的第二定理又否認了第二個命題:「數學是相容的嗎?」app
對於第三問題(可斷定性問題),在1936年,丘奇(Alonzo Church)和艾倫·圖靈分別證實了存在不可解的問題。
圖靈提出的圖靈機模型,而丘奇提出了一個基於lambda演算(lambda calculus)的模型,這兩個模型被圖靈證實是等價的。
圖靈在圖靈機的思想上,繼續思考,逐步設計出早期的計算機(一個英國版的計算機,比馮諾依曼的計算機更早被建造出來。馮諾依曼對圖靈機也是承認的。),而且考慮人工智能的問題。
而lambda演算概念,則被髮揚光大成了函數式編程思想。
偉大的數學!ide
函數式編程是基於表達式(expression)的一種編程範式。
函數式編程將計算視爲對數學函數的求值過程。函數式編程
阿袁和阿靜中午又在一塊兒,繼續討論函數式編程。
「我認爲,咱們能夠把函數式編程理解成在作數學計算。這種編程風格是一種面向表達式(expression-oriented)風格。」,阿靜慢慢地說道。
"我也是這麼想的。因此,做爲一個面向對象的程序員,咱們先要把對象的概念捨去掉。"
「是啊,倒空一些,才能學習到新的知識。」
「咱們怎麼考慮class的做用呢?」
「在面向對象中,class的一個主要做用的封裝。」
「那麼,在函數式編程中,class的做用應該是對算法(函數)的分類了。」
「正解!咱們作一個遊戲,看看若是把一個面向對象的程序,變成面向表達式的程序。」
「好啊,我先用Scala寫一個面向對象的例子。」函數
// 這個例子的主要功能是對一個List排序。 // 這是一個基於面向對象思想的實現。 object Main { // 一個支持排序的class。 // 這個class,須要外部提供一個比較器。 class ListSorter[T](a: List[T]) { def data: List[T] = a def sort(comparer: IComparer[T]): List[T] = { return data.sortWith(comparer.compare) } } // 咱們爲比較器定義一個interface,帶一個比較函數compare。 trait IComparer[T]{ def compare(a: T, b: T): Boolean } // 這個一個具體的比較器,實現了比較器IComparer。 class IntComparer extends IComparer[Int] { override def compare(a: Int, b: Int): Boolean = { return a < b } } // 測試一下 def main(args: Array[String]): Unit = { val list = List(9,1,6,3,5) val sorter = new ListSorter[Int](list) // 在調用sort方法時,傳入一個具體比較器對象。 println(sorter.sort(new IntComparer())) } }
在這個例子中,ListSorter須要外部提供一個比較方法。爲了解決這個問題,面向對象的思路是:學習
- 對外部功能,定義了一個接口。並在接口中,聲明這個比較函數。
- ListSorter的sort函數,經過接口來使用外部的比較方法。
- 外部:定義了一個具體類,實現了這個接口。
- 調用者:在調用ListSorter的sort函數時,傳入一個具體類的對象。
「如今,咱們的任務就是:把這個例子改爲面向表達式的風格。」
「首先,把sort函數的輸入參數comparer變成一個函數類型。」
「這樣,咱們就不須要IComparer,這個接口了。」
「IntComparer就能夠從一個封裝類,變成一個帶比較函數的靜態類。」測試
函數式編程的第一個例子:
// 這個例子的主要功能是對一個List排序。 // 這是一個基於面向表達式的實現。 object Main { // 一個支持排序的class。 // 這個class,須要外部提供一個比較函數。 class ListSorter[T](a: List[T]) { def data: List[T] = a def sort(f: (T, T) => Boolean): List[T] = { return data.sortWith(f) } } // 實現了一個比較函數。 object IntComparer { def compare(a: Int, b: Int): Boolean = { return a < b } } def main(args: Array[String]): Unit = { val list = List(9,1,6,3,5) val sorter = new ListSorter[Int](list) // use function rather than object println(sorter.sort(IntComparer.compare)) // use function with lambda expression println(sorter.sort( (a, b) => a < b )) // use function with underscore println(sorter.sort( _ < _ )) // fluent infix style println(sorter sort IntComparer.compare) // fluent infix style with lambda expression println(sorter sort {(a, b) => a < b}) // fluent infix style with underscore println(sorter sort { _ < _ }) } }
注:這裏面實現了多種風格。
lambda expression,能夠當作匿名函數的實現方法。
underscore: underscore在scala中有多種含義。這裏是一種匿名函數的實現,scala會根據上下文推測"_"的含義。
infix style: 能夠看出,不須要"."了。
「太好了,咱們向函數式編程邁出了第一步!」
「在昨天的例子中,咱們仍是實例化了ListSorter。」
「是啊,按照函數式編程的思想,咱們須要把ListSorter的sort方法當作一個函數。」
「另外,我還學到了一點,在面向表達式風格中,不要寫return。最後一條expression的結果就應該是函數的返回值。」
「嗯,好的,咱們繼續改改看。」
函數式編程的改進版:
// 這個例子的主要功能是對一個List排序。 // 這是一個基於面向表達式的實現。 // * Changed ListSorter as module // * Do not use return object Main { object ListSorter { def sort[T](a: List[T], f: (T, T) => Boolean): List[T] = { // Do not use return a.sortWith(f) } } object IntComparer { def compare(a: Int, b: Int): Boolean = { // Do not use return a < b } } def main(args: Array[String]): Unit = { val list = List(9,1,6,3,5) // use function rather than object println(ListSorter.sort(list, IntComparer.compare)) // use function with lambda println(ListSorter.sort[Int](list, (a, b) => a < b)) // use function with underscore println(ListSorter.sort[Int](list, _ < _)) } }
發現了嗎? fluent infix style沒有了。這是由於,infix操做支持有一個參數的函數。
「fluent infix style有點接近人類的語言,使用好的話,能夠增長可讀性。」
"可是,它也有個限制,只支持有一個參數的函數。"
「其實,卷積能夠解決這個問題。"
"卷積?"
「給你舉個例子。通常的函數是這樣的。」
def normalFunc(a: Int, b: Int, c:Int): Int = { a + b + c }
"而卷積函數變成這樣,參數被分隔一個一個的。"
def curriedFunc(a: Int)(b: Int)(c:Int): Int = { a + b + c }
"卷積的思想是: 每次只給函數的一個參數賦值。這樣的一個主要用途是:局部函數(partial function application),
能夠想象爲把一個計算分紅多個步驟計算(multiple stage computation)。這是調用的方法:"
// Usage: Currying in partial function application val add2OneByOne = curriedFunc(1) _ // call a curried function variable with a normal arugment println(add2OneByOne(2)(3)) // output: 6
「卷積帶來的一個附加益處,就是支持了多參數函數的infix操做。」
// Usage: call a curried function with an expression in fluent infix style println(curriedFunc {1} {2} {3}) // output: 6
「scala真強大啊!咱們繼續改改看。」
// 這個例子的主要功能是對一個List排序。 // 這是一個基於面向表達式的實現。 // * Using currying object Main { object ListSorter { // curried function (a)(b) def sort[T](a: List[T])(f: (T, T) => Boolean): List[T] = { a.sortWith(f) } } object IntComparer { def compare(a: Int, b: Int): Boolean = { a < b } } def main(args: Array[String]): Unit = { val list = List(9,1,6,3,5) // use function rather than object println(ListSorter.sort(list)(IntComparer.compare)) // use function with lambda expression println(ListSorter.sort(list)((a, b) => a < b )) // use function with underscore println(ListSorter.sort(list)(_ < _ )) // fluent infix style println(ListSorter.sort {list} {IntComparer.compare}) // fluent infix style with lambda expression println(ListSorter.sort {list} {(a, b) => a < b}) // fluent infix style with underscore println(ListSorter.sort {list} { _ < _ }) // currying usage: partial function application val sortWith = ListSorter.sort(list) _ // fluent infix style println(sortWith(IntComparer.compare)) // fluent infix style with lambda expression println(sortWith {(a, b) => a < b}) // fluent infix style with underscore println(sortWith { _ < _ }) } }
"今天有個新的認識。在面嚮對象語言中,咱們常用null。可是在數學計算中,null是沒有意義的。"
「那麼要使用什麼呢?」
「若是返回值類型是一個集合,最好返回空集合。」
「若是返回值類型是一個值,scala提供了一個Option的泛型類,提供了一個None對象,表示返回的值是沒有值。」
「代碼示例以下。」
// 這個例子的主要功能是說明使用Nil和None、 object Main { object NilNoneSample { // 使用空集合。不要使用null。 def getEmptyList(): Seq[Int] = { Nil } // 使用空集合。不要使用null。 def getEmptyVector(): Vector[Int] = { Vector() } // 對於可能返回「沒有值」的結果,使用Option泛型類。 def getValueIfLargeThanZero(a: Int): Option[Int] = { if (a > 0) Option(a) else None } } def main(args: Array[String]): Unit = { // use empty collection replace null println(NilNoneSample.getEmptyList) // output: List() println(NilNoneSample.getEmptyVector) // output: Vector() // use None replace null println(NilNoneSample.getValueIfLargeThanZero(1).get) // output: 1 println(NilNoneSample.getValueIfLargeThanZero(-1).isEmpty) // output: true } }
2016年9月X日 星期六
最近和阿靜接觸的機會多了不少。也學會了一些函數式編程的概念。
總結一下:
函數式編程的風格,即面向表達式編程風格,有以下要求: