1. 程式人生 > >Scala 控制結構和函式

Scala 控制結構和函式

0.要點

  • if 表示式有值
  • 塊也有值,是它最後一個表示式的值
  • 分號(在絕大多數情況下)不是必需的
  • void型別是Unit
  • 避免在函式定義中使用return
  • 注意別在函式定義式中漏掉了 =
  • 異常的工作方式和Java或C++中基本一樣,不同的是你在catch語句中使用“模式匹配”。
  • Scala沒有受檢異常

1.條件表示式

if (x > 0) 1 else -1
val s = if(x > 0) 1 else -1 
相當於 Java 或 C++:s = x > 0 ? 1 : -1

表示式if(x>0) 1 else -1的型別為Int,因為2個分支的型別都是Int;
if(x>0) "positive" else -1

的型別為2個分支型別的公共超型別。本例中,其中一個型別是java.lang.String,另一個型別是Int,它們的公共超型別叫做Any。
else 部分缺失:if ( x > 0 ) 1
在Scala中,每個表示式都應該有某種值。這個問題的解決方案是引入一個Unit類,寫做()。
if(x > 0) 1 等同於 if(x > 0) 1 else ()
()是表示“空值”的佔位符,將Unit當做Java或C++中的void。

3.塊表示式和賦值

{ }塊包含一系列表示式,其結果也是一個表示式。塊中最後一個表示式的值就是塊的值。

val distance = {
val
dx = x-x0; val dy = y - y0; sqrt(dx * dx + dy * dy) }

在Scala中,賦值動作本身是沒有值的——它的值是Unit型別,賦值語句結束的塊,如:

{
 r = r * n;
 n -= 1
}

它的值是Unit型別。

x = y =1 # 不能這樣
y = 1的值是(),所以x=()
在Java和C++中,賦值語句的值是被賦的那個值,所以可以將賦值語句串接在一起。

5.迴圈

while(n > 0){
    r = r * n
    n -= 1
}
for(i <- 1 to n)
    r = r * i

val s = "Hello"
var sum = 0 for(i <- 0 until s.length) //i的最後一個取值是s.length-1 sum += s(i) var sum = 0 for(ch <- "Hello") sum +=ch

中途退出迴圈:
1. 使用Boolean型的控制變數
2. 使用巢狀函式——從函式中return
3. 使用Breaks物件中的break方法

6.高階for迴圈和for推導式

for(i <- 1 to 3;j<- -1 to 3) print ((10 * i + j) + " ")
// 11 12 13 21 22 23 31 32 33
for(i <- 1 to 3;j<- 1 to 3 if i != j){
print ((10*i +j) + " ")
} 
//12 13 21 23 31 32 //if前沒有分號
for(i <- 1 to 3;from = 4 - i;j <- from to 3){
    print((10 * i + j) + " ")
}
// 13 22 23 31 32 33

如果for迴圈的迴圈體以yield開始,則該迴圈會構造出一個集合,每次迭代生成集合中的一個值:

for(i <- 1 to 10) yield i % 3
//生成Vector(1,2,0,1,2,0,1,2,0,1)

for推導式生成的集合與它的第一個生成器是型別相容的。
for(c <- "Hello";i <- 0 to 1) yield (c+i).toChar
// "HIeflmlmop"
for(c <- 0 to 1;c <- "Hello") yield (c+i).toChar
//Vector('H','e','l','l','o','I','f','m','m','p')

7.函式

定義函式:要給出函式名,輸入引數,返回值型別,函式體
返回型別當函式是遞迴時指出。
Scala編譯器可以通過=符號右側的表示式的型別推斷出返回型別。

def abs(x:Double) = if (x >= 0) x else -x

def fac(n:Int) = {
var r =1 
for (i <- 1 to n) r = r * i
r
}

//遞迴
def fac(n:Int):Int = if(n <= 0) 1 else n * fac(n-1)
//如果沒有返回型別,Scala編譯器無法校驗n*fac(n-1)的型別是Int。

8.預設引數和帶名引數

def decorate(str:String, left:String="[", right:String = "]") = left + str + right

引數left和right帶有預設值”[“和”]”

decorate("Hello")  => "[Hello]"
decorate("Hello","<<<",">>>")=>"<<<Hello>>>"

9.變長引數

def sum(args:Int*)={
    var result = 0
    for (arg <- args) result += arg
    result
}
val s = sum(1,4,9,16,25)
//函式得到的是一個型別為Seq的引數,可以用for迴圈來訪問每一個元素。
val s = sum(1 to 5) //錯誤
val s = sum(1 to 5:_*)//將1 to 5當做引數序列處理

遞迴定義中:

def recursiveSum(args:Int*):Int = {
    if (args.length == 0) 0
    else args.head+recursiveSum(args.tail:_*)
}
序列的head是它的首個元素,tail是所有其他元素的集合序列,這也是個Seq,用_*將它轉換成引數序列。

10.過程

如果函式體包含在{ }中,但沒有前面的=號,那麼返回型別就是Unit。這樣的函式稱為過程(procedure)

def box(s:String){//沒有等號
var border = "-" * s.length + "--\n"
println(border + "|" + s + "|\n" + border)
}

也可以顯式定義Unit返回型別:

def box (s:String):Unit = {
...
}

11.懶值

首次對它取值時才初始化

lazy val words = scala.io.Source.fromFile(“/usr/share/dict/words”).mkString

12.異常

throw new IllegalArgumentException(“x should not be negative”)
throw表示式有特殊的型別Nothing。這在if/else中很有用。如果一個分支的型別是Nothing,那麼if/else表示式的型別就是另一個分支的型別。
如:

if( x >= 0){
    sqrt(x)
}else throw new IllegalArgumentException("x should not be negative")
//第一個分支的型別是Double,第二個分支的型別是Nothing,因此,if/else表示式的型別是Double。

捕獲異常的語法採用的是模式匹配的語法:

try{
    process(new URL("http://horstmann.com/fred-tiny.gif"))
}catch{
    case _: MalformedURLException => println("Bad URL:" + url)
    case ex:IOException => ex.printStackTrace()
}

更通用的異常應該在更具體的異常之後。
try/catch語句處理異常,try/finally語句在異常沒有被處理時執行某種動作。