有趣的 Scala 語言:簡潔的 Scala 語法

一、Scala開發環境

搭建Scala 開發環境,一是在IntelliJ IDEA 上安裝Scala 插件和安裝Scala SDK,具體操作步驟可以參考Scala專欄文章。

二是通過Scala RELP(Read-Eval-Print Loop)交互式環境,該交互式環境適合代碼簡單調試,不太適合進行應用開發。

二、變量和函數

定義變量時沒有指定變量類型。這是否意味著 Scala 是和 Python 或者 Ruby 一樣的動態類型語言呢?恰恰相反,Scala 是嚴格意義上的靜態類型語言,由於其採用了先進的類型推斷(Type Inference)技術,程序員不需要在寫程序時顯式指定類型,編譯器會根據上下文推斷出類型信息。比如變量 x被賦值為 0,0 是一個整型,所以 x的類型被推斷出為整型。當然,Scala 語言也允許顯示指定類型,如變量 x1,y1的定義。一般情況下,我們應儘量使用 Scala 提供的類型推斷系統使代碼看上去更加簡潔。

另一個發現是程序語句結尾沒有分號,這也是 Scala 中約定俗成的編程習慣。大多數情況下分號都是可省的,如果你需要將兩條語句寫在同一行,則需要用分號分開它們。

函數的定義也非常簡單,使用關鍵字 def,後跟函數名和參數列表,如果不是遞歸函數可以選擇省略函數返回類型。Scala 還支持定義匿名函數,匿名函數由參數列表,箭頭連接符和函數體組成。函數在 Scala 中屬於一級對象,它可以作為參數傳遞給其他函數,可以作為另一個函數的返回值,或者賦給一個變量。在下面的示例代碼中,定義的匿名函數被賦給變量 cube。匿名函數使用起來非常方便,比如 List對象中的一些方法需要傳入一個簡單的函數作為參數,我們當然可以定義一個函數,然後再傳給 List對象中的方法,但使用匿名函數,程序看上去更加簡潔。


// 定義函數

def square(x: Int): Int = x * x

// 如果不是遞歸函數,函數返回類型可省略

def sum_of_square(x: Int, y: Int) = square(x) + square(y)

sum_of_square(2, 3)

有趣的 Scala 語言:簡潔的 Scala 語法


// 定義匿名函數

val cube = (x: Int) => x * x *x

cube(3)

// 使用匿名函數,返回列表中的正數

List(-2, -1, 0, 1, 2, 3).filter(x => x > 0)

有趣的 Scala 語言:簡潔的 Scala 語法


讓我們再來和 Java 中對應的函數定義語法比較一下。首先,函數體沒有像 Java 那樣放在 {}裡。Scala 中的一條語句其實是一個表達式,函數的執行過程就是對函數體內的表達式的求值過程,最後一條表達式的值就是函數的返回值。如果函數體只包含一條表達式,則可以省略 {}。其次,沒有顯示的 return語句,最後一條表達式的值會自動返回給函數的調用者。

和 Java 不同,在 Scala 中,函數內部還可以定義其他函數。比如上面的程序中,如果用戶只對 sum_of_square 函數感興趣,則我們可以將 square 函數定義為內部函數,實現細節的隱藏。


定義內部函數:

有趣的 Scala 語言:簡潔的 Scala 語法


三、流程控制語句

複雜一點的程序離不開流程控制語句,Scala 提供了用於條件判斷的 if else和表示循環的 while。和 Java 中對應的條件判斷語句不同,Scala 中的 if else是一個表達式,根據條件的不同返回相應分支上的值。比如下面例子中求絕對值的程序,由於 Scala 中的 if else是一個表達式,所以不用像 Java 那樣顯式使用 return返回相應的值。


使用 if else 表達式:

def abs(n: Int): Int = if (n > 0) n else -n

有趣的 Scala 語言:簡潔的 Scala 語法


和 Java 一樣,Scala 提供了用於循環的 while 語句,在下面的例子中,我們將藉助 while 循環為整數列表求和。

使用 while 為列表求和:

def sum(xs: List[Int]) = {

var total = 0

var index = 0

while (index < xs.size) {

total += xs(index)

index += 1

}

total

}

有趣的 Scala 語言:簡潔的 Scala 語法


上述程序是習慣了 Java 或 C++ 的程序員想到的第一方案,但仔細觀察會發現有幾個問題:首先,使用了 var定義變量,我們在前面說過,儘量避免使用 var。其次,這個程序太長了,第一次拿到這個程序的人需要對著程序仔細端詳一會:程序首先定義了兩個變量,並將其初始化為 0,然後在 index小於列表長度時執行循環,在循環體中,累加列表中的元素,並將 index加 1,最後返回最終的累加值。直到這時,這個人才意識到這個程序是對一個數列求和。


讓我們換個角度,嘗試用遞歸的方式去思考這個問題,對一個數列的求和問題可以簡化為該數列的第一個元素加上由後續元素組成的數列的和,依此類推,直到後續元素組成的數列為空返回 0。具體程序如下,使用遞歸,原來需要 9 行實現的程序現在只需要兩行,而且程序邏輯看起來更清晰,更易懂。


使用遞歸對數列求和:

//xs.head 返回列表裡的頭元素,即第一個元素

//xs.tail 返回除頭元素外的剩餘元素組成的列表

def sum1(xs: List[Int]): Int = if (xs.isEmpty) 0 else xs.head + sum1(xs.tail)


有沒有更簡便的方式呢?答案是肯定的,我們可以使用列表內置的一些方法達到同樣的效果:

有趣的 Scala 語言:簡潔的 Scala 語法


上述使用了規約操作。

規約操作是對容器的元素進行兩兩運算,將其規約為一個值。最常見的規約方式使 reduce,它接受一個二元函數 f 作為參數,首先將 f 作用在某兩個元素上並返回一個值,然後再將 f 作用在上一個返回值和容器的下一個元素上,再返回一個值,依次類推,最後容器中的所有值會被規約為一個值。


list map (_.toString) reduce((x,y)=>s"f($x,$y)")

有趣的 Scala 語言:簡潔的 Scala 語法

上面這行代碼:先通過 map 操作將List[Int] 轉化成 List[String],也就是把列表中的每個元素從 Int 類型轉換成 String 類型,然後對這個字符串進行自定義規約,語句的執行結果清楚地展示了 reduce的過程。


事實上,List 已經為我們提供了 sum 方法,在實際應用中,我們應該使用該方法,而不是自己定義一個。作者只是希望通過上述例子,讓大家意識到 Scala 雖然提供了用於循環的 while 語句,但大多數情況下,我們有其他更簡便的方式能夠達到同樣的效果。

有趣的 Scala 語言:簡潔的 Scala 語法


有趣的 Scala 語言:簡潔的 Scala 語法


四、如何運行 Scala 程序?

在運行方式上,Scala 又一次體現出了它的靈活性。它可以被當作一種腳本語言執行,也可以像 Java 一樣,作為應用程序執行。


作為腳本執行:

我們可以將 Scala 表達式寫在一個文件裡,比如 Hello.scala。在命令行中直接輸入 scala Hello.scala就可得到程序運行結果。

Hello.scala 代碼:

println("Hello Rickie!")

有趣的 Scala 語言:簡潔的 Scala 語法


作為應用程序執行:

作為應用程序執行時,我們需要在一個單例對象中定義入口函數 main,經過編譯後就可以執行該應用程序了。

object HelloRickie {

def main(args: Array[String]): Unit = {

println("Hello Rickie!")

}

}

有趣的 Scala 語言:簡潔的 Scala 語法


Scala 還提供了一個更簡便的方式,直接繼承另一個對象 App,無需定義 main方法,編譯即可運行。

有趣的 Scala 語言:簡潔的 Scala 語法


五、結束語

本文為大家介紹了 Scala 的基本語法,相比 Java,Scala 的語法更加簡潔,比如 Scala 的類型推斷可以省略程序中絕大多數的類型聲明,短小精悍的匿名函數可以方便的在函數之間傳遞,還有各種在 Scala 社區約定俗成的習慣,比如省略的分號以及函數體只有一條表達式時的花括號,這一切都幫助程序員寫出更簡潔,更優雅的程序。

有趣的 Scala 語言:簡潔的 Scala 語法


分享到:


相關文章: