Scala如何改變了我的編程風格:從命令式到函數(shù)式

51CTO編輯推薦:
Scala編程語言專題

【51CTO快譯】編者前言:這篇文章最初寫于2008年底,作者Bill Venners一方面是美國著名開發(fā)網(wǎng)站Artima的總編,另一方面也是一位十分關注Scala語言的Java程序員。在這幾個月間的Scala創(chuàng)始人Martin Odersky訪談系列中,與Martin對話的正是Bill Venners。這篇文章雖然已經(jīng)完成了半年有余,但對于還不很熟悉Scala語言的Java程序員而言,仍然是一篇非常實用的Scala語言簡介。以下是譯文:

每次我學習一門新的語言,我都會學到某些編程方面的東西。比如說,當我以一個C++程序員的身份學習Java的時候,Java的接口構造教會我來自純粹的抽象基類的多重繼承的價值。盡管在C++里面這種編程風格是有可能的,但在我使用C++的日子里,我卻沒有考慮用這種方式進行多重繼承,而我在C++設計中也不怎么使用抽象基類。然而,一旦我開始進行Java編程,我就開始一直使用這種風格了。學習Java—尤其是它的接口構造—改變了我OO設計的方法。

51CTO編輯推薦:
充分利用面向?qū)ο笳Z言的接口特性
|
面向?qū)ο蟮乃季S過程

我學習
Scala編程的時候也發(fā)生了類似的情況。在過去的兩年里,我有相當多的時間是用
Scala工作的,Scala是Java平臺上的一種新的靜態(tài)類型語言,它融合了面向?qū)ο缶幊毯秃瘮?shù)型程序設計的概念。Scala能讓我寫出幾乎跟Ruby和Python
一樣簡潔的代碼。在
Scala
我可以跟在
Java
里面一樣方便地調(diào)用
Java
庫,包括我已有的
Java
庫。考慮到
Scala
是靜態(tài)類型的,我可以享受到諸多靜態(tài)類型的好處,諸如將文檔作為類型,
IDE
代碼自動完成,動態(tài)代碼重構(
deterministic refactoring
)以及執(zhí)行速度等(
Scala
程序的執(zhí)行速度跟
Java
的一樣快)。但
Scala
還讓我以簡潔和類型安全的方式獲得某些通常是動態(tài)語言的好處,例如在已有類上增加新方法的能力,或者將類型傳遞給沒有共同繼承關系的方法。

Scala
是怎樣改變了我對編程的看法的呢?一句話:我學會了欣賞函數(shù)化的風格。函數(shù)化的編程風格強調(diào)不可變對象、變量可被初始化但不能重新賦值(
Java
中的最終變量)、數(shù)據(jù)結構轉(zhuǎn)換,以及方法和控制的構造,最終產(chǎn)生一個沒有副作用的結果。這個領域的另一端是命令式的風格,以可變對象、變量可被重新賦值(
Java
里的正常變量)、在數(shù)據(jù)結構中索引、以及帶副作用的方法和控制構造為特征。

盡管
Scala
經(jīng)常被吹捧為函數(shù)型編程語言,當它并不僅僅是函數(shù)型的。
Scala
同時支持函數(shù)式和命令式兩種風格。如果你自己選擇要這么做的話,你可以以
Java
的編程方式進行
Scala
編程,那種風格主要是命令式的。這樣有助于
Scala
的學習曲線變緩,但隨著對
Scala
越來越熟悉,你就會發(fā)現(xiàn)自己會更喜歡函數(shù)式的。我就是這樣。為什么?因為我發(fā)現(xiàn)函數(shù)型風格往往要比命令式風格的代碼更簡潔,且更不易出錯。函數(shù)式風格的代碼通常層次更高,這使得它編寫起來更快,閱讀也更為容易。舉個例子,看看下面這段確定一個字符串是否包含大寫字符的
Java
代碼。

boolean nameHasUpperCase = false; // 這是Java
for (int i = 0; i < name.length(); ++i) {

    if (Character.isUpperCase(name.charAt(i))) {

        nameHasUpperCase = true;

        break;

    } 

}

這里的命令式風格是很明顯的,因為
nameHasUpperCase
變量被重新賦值會給
loop
循環(huán)帶來副作用,
loop
是通過字符串中的字母索引進行迭代的。在
Java
你還可以以更為簡潔的方式得到相同的結果,像下面這樣:

boolean nameHasUpperCase = !name.toLowerCase().equals(name);

這一行
Java
代碼展現(xiàn)出一種更為函數(shù)化的風格,因為它轉(zhuǎn)換不可變數(shù)據(jù):
name
這個字符串被轉(zhuǎn)換為另外一個全部字母都是小寫的字符串,然后值被轉(zhuǎn)換為布爾結果。此外,
nameHasUpperCase
這個變量被初始化了,但僅限于這一小塊代碼里,而沒有被重新賦值。如果該變量為最終值的話,它的函數(shù)化就會更為清晰。


Scala
里面,你可以寫出跟以上兩個例子類似的代碼,不過更為理想的編寫方式是像下面這樣的:

val nameHasUpperCase = name.exists(_.isUpperCase)

nameHasUpperCase
變量被定義為
val
,即可被初始化但不能被重新賦值的變量(類似于
Java
里面的最終變量)。甚至于盡管本例中并無顯式的類型標注,
Scala
的類型推斷機制也會給
nameHasUpperCase
賦予
Boolean
類型。
exists
方法在對象集合中迭代,并依次將每個元素傳遞給函數(shù)對象。在這里,
name
字符串被視為字符集合,因此
exists
會把字符串的每一個字符都傳遞給該函數(shù)。
_.isUpperCase
的語法是
Scala
里的一種函數(shù)顯式聲明(
function literal
),是一種編寫少量代碼就可以到處傳遞和調(diào)用的速寫方式。下劃線代表該函數(shù)的唯一參數(shù)。因此你可以把下劃線視為每次該函數(shù)被調(diào)用時待填的空白。如果
exists
方法發(fā)現(xiàn)該函數(shù)因被傳遞的字符中的其中一個而返回
true—
比如說,其中一個字符是大寫的

而返回
true
。否則就返回
false
。

盡管最后的這個單行代碼對于某些不熟悉Scala的人來說像是天書,只要你了解了
Scala,你就能一眼看出代碼的目的。相反,其他的兩個版本卻要費上一點功夫去研究一下。另外需要注意的一點不同是命令式例子中潛在的偏移錯誤,因為你必須顯式地指出迭代的上標。在函數(shù)化的版本里這種錯誤不會產(chǎn)生,在這種方式下,函數(shù)化版本相對而言不易出錯。

最后,我想指出的是我轉(zhuǎn)向
Scala
的時候并沒有“徹底函數(shù)化”。盡管我已經(jīng)發(fā)現(xiàn)通常大部分情況下函數(shù)化風格的代碼來得更為簡潔、明晰,更不易出錯,我還發(fā)現(xiàn)有時候命令式風格也可帶來更為清晰和簡潔的代碼。在那種情況下,我就會使用命令式的。
Scala
允許我方便地應用函數(shù)式和命令式的風格,結合使用此二者,我就能找到寫出清晰代碼的最佳方式。

函數(shù)式編程和命令式編程簡介

什么是函數(shù)式編程?(參考資料:《征服RIA:基于JavaScript的Web客戶端開發(fā)》第8章JavaScript函數(shù)對象
在數(shù)學領域,函數(shù)是一種關系,這種關系使一個集合里的每一個元素對應到另一個集合里的唯一元素。函數(shù)是將唯一的輸出值賦予每一輸入的"法則"。這一"法則"可以用函數(shù)表達式、數(shù)學關系,或者一個將輸入值與輸出值對應列出的簡單表格來表示。函數(shù)最重要的性質(zhì)是其決定性,即同一輸入總是對應同一輸出(注意,反之未必成立)。從這種視角,可以將函數(shù)看做"機器"或者"黑盒",它將有效的輸入值變換為唯一的輸出值。通常將輸入值稱做函數(shù)的參數(shù),將輸出值稱做函數(shù)的值。
《Why Functional Programming Matters》的作者John Hughes 說明了模塊化是成功編程的關鍵,而函數(shù)編程可以極大地改進模塊化。在函數(shù)編程中,編程人員有一個天然框架用來開發(fā)更精練的、更小的、更簡單的和更一般化的模塊,然后將它們組合在一起。函數(shù)式編程的基本特點是:
豐富的數(shù)據(jù)類型;
函數(shù)是運算元;
在函數(shù)內(nèi)保存數(shù)據(jù);
函數(shù)內(nèi)的運算對函數(shù)外無副作用。
函數(shù)式編程只描述在程序輸入上執(zhí)行的操作,不必使用臨時變量保存中間結果。重點是捕捉"是什么以及為什么",而不是"如何做"。與將重點放在執(zhí)行連續(xù)命令上的過程性編程相比,函數(shù)式編程的重點是函數(shù)的定義而不是狀態(tài)機(State Machine)的實現(xiàn)。是一種強調(diào)表達式的計算而非命令的執(zhí)行的一種編程風格。表達式是用函數(shù)結合基本值構成的,它類似于用參數(shù)調(diào)用函數(shù)(函數(shù)式的優(yōu)美的說明可見《Functional Programming For The Rest of Us》)。
什么是命令式編程?(參考資料:維基百科)
命令式編程,是種描述電腦所需作出的行為的編程典范。幾乎所有電腦的硬體工作都是命令式的;幾乎所有電腦的硬體都是設計來執(zhí)行機器碼,使用命令式的風格來寫的。較高階的命令式編程語言使用變數(shù)和更復雜的語句,但仍依從相同的典范。食譜和行動清單,雖非電腦程式,但與命令式編程有相似的風格:每步都是指令,有形的世界控制情況。因為命令式編程的基礎觀念,不但概念上比較熟悉,而且較容易具體表現(xiàn)于硬體,所以大部分的編程語言都是命令式的。
原文:How Scala Changed My Programming Style
作者:Bill Venners

【相關閱讀】
Scala的類型系統(tǒng) 比Java更靈活
Java程序員,你為什么要關注Scala

Scala創(chuàng)始人:創(chuàng)造比Java更好的語言

Java以外的選擇 Scala編程語言簡介

Java之外,選擇Scala還是Groovy?

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容