Scala學習總結
一、Scala簡介
Scala特點:
- Scala 是一門多范式 (multi-paradigm) 的編程語言 ,設計初衷是要集成面向物件編程和函式式編程的各種 特性,
- Scala 是一門以 java 虛擬機 (JVM) 為運行環境的編程語言 ,Scala 源代碼(.scala)會被編譯成 Java 位元組碼(.class) ,然后運行于 JVM 之上 ,并可以呼叫現有的 Java 類別庫 ,實作兩種語言的無縫對接, 強型別語言
- 簡潔高效 (各種語法糖)
- 源于java ,與java對比學習 ,更易掌握
二、變數
1. 基本語法
先宣告再使用:
|
val/var 變數名 [:變數型別] = 變數值 |
注意: 變數型別可以省略 ,變數型別確定后就無法改變 (強型別語言) var修飾的變數可以改變 ,val修飾的變數 不可變, val是執行緒安全的 ,效率更高 ,val修飾的變數在編譯后 ,等同于加上了?nal ,
變數宣告時需要初始值,
2. 資料型別
Scala 與 Java 有著相同的資料型別 ,在 Scala 中資料型別都是物件 ,也就是說 scala 沒有 java 中的原生類 型, Scala 資料型別分為兩大類 AnyVal(值型別) 和 AnyRef(參考型別) , 注意:不管是 AnyVal 還是 AnyRef 都是物件,
2.1. 資料型別一覽圖

說明:
1. Any 是所有類的根型別,即所有類的父類(基類),
2. 在 scala 中類分為兩個大的型別分支(AnyVal [值型別 ,即可以理解成就是 java 的基本資料型別], AnyRef 型別),
3. 在 AnyVal 雖然叫值型別 ,但是仍然是類(物件),
4. 在 scala 中有兩個特別的型別(Null ), 還有一個是 Nothing,
5. Null 型別只有一個實體 null, 他是 bottom class ,是 AnyRef 的子類,
6. Nothing 型別是所有類的子類 , 它的價值是在于因為它是所有類的子類 ,就可以將 Nothing 型別的物件回傳 給任意的變數或者方法 ,比如案例:
|
def f1():Nothing= { //表示 f1 方法就是沒有正常的回傳值 ,專門用于回傳例外 throw new Exception("例外發生") } |
7. 在 scala 中仍然遵守 低精度的資料自動的轉成 高精度的資料型別,
8. 在 scala 中 , Unit 型別比較特殊 ,這個型別也只有一個實體 () ,
2.2. 整數型別
2.2.1 整數型別的分類
|
資料型別 |
描述 |
|
Byte |
8位有符號補碼整數,數值區間為 -128 到 127 |
|
Short |
16位有符號補碼整數,數值區間為 -32768 到 32767 |
|
Int |
32位有符號補碼整數,數值區間為 -2147483648 到 2147483647 |
|
Long |
64位有符號補碼整數,數值區間為 -9223372036854775808 到 9223372036854775807 |
2.2.2 整型的使用細節
Scala 各整數型別有固定的表數范圍和欄位長度 ,不受具體 OS 的影響 ,以保證 Scala 程式的可移植性,
Scala 的整型 常量/字面量 默認為 Int 型 ,宣告 Long 型 常量/字面量 須后加‘l’’或‘L’ ,
Scala 程式中變數常宣告為 Int 型 ,除非不足以表示大數 ,才使用 Long,
2.3. 浮點型別
2.3.1 浮點型別的分類
|
資料型別 |
描述 |
|
Float |
32 位, IEEE 754 標準的單精度浮點數 |
|
Double |
64 位, IEEE 754 標準的雙精度浮點數 |
2.3.2 浮點型使用細節
與整數型別類似 ,Scala 浮點型別也有固定的表數范圍和欄位長度 ,不受具體 OS 的影響, Scala 的浮點型常量默認為 Double 型 ,宣告 Float 型常量 ,須后加‘f’或‘F’,
通常情況下 ,應該使用 Double 型 ,因為它比 Float 型更精確(小數點后大致 7 位),
2.4. 字符型別(Char)
字符常量是用單引號(‘ ’)括起來的單個字符,例如:var c1 = 'a‘ var c2 = '中‘ var c3 = '9' 2) Scala 也允許使用轉 義字符‘\’來將其后的字符轉變為特殊字符型常量,例如:var c3 = ‘\n’ // '\n' 表示換行符可以直接給 Char 賦一個整數 ,然后輸出時 ,會按照對應的 unicode 字符輸出 ['\u0061' 97] Char 型別是可以進行運算的 ,相當于一個整數 ,因為它都對應有 Unicode 碼,
2.5. 布爾型別(Boolean)
布爾型別也叫 Boolean 型別 , Booolean 型別資料只允許取值 true 和 false
boolean 型別占 1 個位元組, boolean 型別適于邏輯運算 ,一般用于程式流程控制
2.6. Unit 型別、 Null 型別和 Nothing 型別
|
資料型別 |
描述 |
|
Unit |
表示無值 ,和其他語言中void等同,用作不回傳任何結果的方法的結果型別, Unit只有一個實 例值 ,寫成() |
|
Null |
null 或空參考 |
|
Nothing |
Nothing型別在Scala的類層級的最底端;它是任何其他型別的子型別 |
2.6.2 使用細節
Unit 型別用來標識程序 ,也就是沒有明確回傳值的函式, 由此可見 , Unit 類似于 Java 里的void, Unit 只有 一個實體 ,() ,這個實體也沒有實質的意義
Null 類只有一個實體物件 , null ,類似于 Java 中的 null 參考, null 可以賦值給任意參考型別(AnyRef) ,但是 不能賦值給值型別(AnyVal: 比如 Int, Float, Char, Boolean, Long, Double, Byte, Short)
Nothing ,可以作為沒有正常回傳值的方法的回傳型別 ,非常直觀的告訴你這個方法不會正常回傳 ,而且由于 Nothing 是其他任意型別的子類 ,他還能跟要求回傳值的方法兼容,
二、運算子
1. 運算子介紹
運算子是一種特殊的符號 ,用以表示資料的運算、賦值和比較等,
算術運算子
賦值運算子
關系運算子
邏輯運算子
位運算子
2. 運算子一覽表
2.1. 算術運算子
假定變數 A 為 10 , B 為 20:
|
運算子 |
描述 |
實體 |
|
+ |
加號 |
A + B 運算結果為 30 |
|
- |
減號 |
A - B 運算結果為 -10 |
|
* |
乘號 |
A * B 運算結果為 200 |
|
/ |
除號 |
B / A 運算結果為 2 |
|
% |
取余 |
B % A 運算結果為 0 |
2.2. 賦值運算子
Scala 中沒有++、--運算子 ,需要通過+=、-=來實作同樣的效果
|
運算子 |
描述 |
實體 |
|
= |
簡單的賦值運算 ,指定右邊運算元賦值給左邊的運算元, |
C = A + B 將 A + B 的運算結果賦 值給 C |
|
+= |
相加后再賦值 ,將左右兩邊的運算元相加后再賦值給左邊的 運算元, |
C += A 相當于 C = C + A |
|
-= |
相減后再賦值 ,將左右兩邊的運算元相減后再賦值給左邊的 運算元, |
C -= A 相當于 C = C - A |
|
*= |
相乘后再賦值 ,將左右兩邊的運算元相乘后再賦值給左邊的 運算元, |
C *= A 相當于 C = C * A |
|
/= |
相除后再賦值 ,將左右兩邊的運算元相除后再賦值給左邊的 運算元, |
C /= A 相當于 C = C / A |
|
%= |
求余后再賦值 ,將左右兩邊的運算元求余后再賦值給左邊的 運算元, |
C %= A is equivalent to C = C % A |
|
<<= |
按位左移后再賦值 |
C <<= 2 相當于 C = C << 2 |
|
>>= |
按位右移后再賦值 |
C >>= 2 相當于 C = C >> 2 |
|
&= |
按位與運算后賦值 |
C &= 2 相當于 C = C & 2 |
|
^= |
按位異或運算子后再賦值 |
C ^= 2 相當于 C = C ^ 2 |
|
|= |
按位或運算后再賦值 |
C |= 2 相當于 C = C | 2 |
2.3. 關系運算子
關系運算子的結果都是 Boolean 型 ,也就是要么是true ,要么是 false,關系運算子組成的運算式 ,我們稱為關系 運算式, 如果兩個浮點數進行比較 ,應當保證資料型別一致.
|
運算子 |
描述 |
實體 |
|
== |
等于 |
(A == B) 運算結果為 false |
|
!= |
不等于 |
(A != B) 運算結果為 true |
|
> |
大于 |
(A > B) 運算結果為 false |
|
< |
小于 |
(A < B) 運算結果為 true |
|
>= |
大于等于 |
(A >= B) 運算結果為 false |
|
<= |
小于等于 |
(A <= B) 運算結果為 true |
2.4. 邏輯運算子
假定變數 A 為 1 , B 為 0:
|
運算子 |
描述 |
實體 |
|
&& |
邏輯與 |
(A && B) 運算結果為 false |
|
| | |
邏輯或 |
(A | | B) 運算結果為 true |
|
! |
邏輯非 |
!(A && B) 運算結果為 true |
2.5. 位運算子
位運算子用來對二進制位進行操作
|
運算子 |
描述 |
|
& |
按位與運算子 |
|
| |
按位或運算子 |
|
^ |
按位異或運算子 |
|
~ |
按位取反運算子 |
|
<< |
左移動運算子 |
|
>> |
右移動運算子 |
|
>>> |
無符號右移 |
三、程式流程控制
1. if - else
Scala 中任意運算式都是有回傳值的 ,也就意味著 if else 運算式其實是有回傳結果的 ,具體回傳結果的值取 決于滿 足條件的代碼體的最后一行內容,Scala 中是沒有三元運算子 ,但是可以利用這個特性使用if--else進行三元運算,
例如:
|
val num = StdIn.redInt() val res = if (num > 3) "比3大" else "比三小" println(res) |
1.1. 單分支
|
if (條件運算式) { 執行代碼塊 } |
1.2. 雙分支
|
if (條件運算式) { 執行代碼塊1 } else { 執行代碼塊2 } |
1.3. 多分支
|
if (條件運算式) { 執行代碼塊1 } else if (條件運算式) { 執行代碼塊2 } else if (條件運算式) { 執行代碼塊3 } ... |
1.4. 嵌套分支
|
if (條件運算式) { if (條件運算式) { 執行代碼塊1 } else { 執行代碼塊2 } } |
2. for 回圈
2.1. 范圍資料回圈方式
2.1.1 to方式
|
for(i < ‐ 1 to 3) { // 這里的 1 to 3 也可以是一個集合 前后閉合 (包括 1 和 3) print(i + " ") } |
2.1.2 until方式
|
for(i < ‐ 1 until 3) { //i的取值是1 和 2 ,until表示 前閉后開 的范圍 print(i + " ") } |
2.2. 回圈守衛
回圈守衛 ,即回圈保護式,保護式為 true 則進入回圈體內部 ,為 false 則跳過 ,類似于 continue,
|
for(i < ‐ 1 to 3 if i != 2) { //輸出1 3 print(i + " ") } |
2.3. 引入變數
|
for(i < ‐ 1 to 3; j = 4 ‐ i) {//沒有關鍵字 ,所以要加 ; 隔斷邏輯 print(j + " ") } |
2.4. 嵌套回圈
|
for(i < ‐ 1 to 3; j < ‐ 1 to 3) { println(" i =" + i + " j = " + j) } // 等價于 // 在業務復雜時使用 for (i < ‐ 1 to 3) { for (j < ‐1 to 3) { println(" i =" + i + " j = " + j) } } |
2.5. 回圈回傳值(yield)
將遍歷程序中處理的結果回傳到一個新的Vector集合中 ,使用yield關鍵字 ,yield可以寫代碼塊,
|
val res = for(i < ‐ 1 to 10) yield i * 2 println(res) |
2.6. 控制步長
for回圈的步長控制有兩種方法 ,通常使用回圈守衛的方式,
例:遍歷 1-10, 步長為 3
2.6.1 Range
Range是一個集合 ,括號里面三個數表示: 1: start , 10: end 遍歷到 (end -1) ,3: 表示 step
|
for (i < ‐ Range(1,10,3)) { //遍歷1 ‐ (10 ‐1),步長3 until println("i=" + i) } |
2.6.2 使用守衛
|
for (i < ‐ 1 to 10 if i % 3 == 1 ) { println("i=" + i) } |
3. while 回圈
特點:
- while 回圈是先判斷再執行陳述句,
- 與 If 陳述句不同 ,While 陳述句本身沒有值 ,即整個 While 陳述句的結果是 Unit 型別的()
- 因為while 中沒有回傳值,所以當要用該陳述句來計算并回傳結果時,就不可避免的使用變數 ,而變數需要宣告在 while 回圈的外部 ,那么就等同于回圈的內部對外部的變數造成了影響 ,所以不推薦使用 ,而是推薦使用for 回圈,
語法:
|
while (回圈條件) { 回圈體(陳述句) 回圈變數迭代 } |
4. do..while 回圈
|
回圈變數初始化; do{ 回圈體(陳述句) 回圈變數迭代 } while(回圈條件) |
5. while 回圈的中斷
Scala 內置控制結構特地去掉了 break 和 continue ,是為了更好的適應函式化編程 ,推薦使用函式式的風格解決 break 和 contine 的功能 ,而不是一個關鍵詞,
5.1. 使用breakable控制回圈的中斷
|
//使用前需要導包 import util.control.Breaks._ //將需要通過breakable控制的代碼放到breakable的大括號中 //相當于break,跳出整個回圈 breakable { for (i < ‐ 1 to 10) { if (i == 5) { break() } println("i=" + i) } } //相當于continue,跳出本次回圈 ,繼續執行下一次回圈 for (i < ‐ 1 to 10) { breakable { if (i == 5) { break() } println("i=" + i) } } |
5.2. 使用if-else或回圈守衛實作continue效果
|
//當i=4,5時跳過 for(i < ‐ 1 to 10){ if (i != 4 && i != 5) { println("i=" + i) } } for (i < ‐ 1 to 10 if (i != 4 && i != 5)) { println("i=" + i) } |
四、函式式編程
1. 函式式編程介紹
函式式編程是一種"編程范式" (programming paradigm) ,它屬于結構化編程的一種 ,主要思想是把運算程序盡 量寫成一系列嵌套的函式呼叫,函式式編程中 ,將函式也當做資料型別 ,因此可以接受函式當作輸入 (引數) 和輸 出 (回傳值) ,(增強了編程的粒度)
在 Scala 中 ,方法和函式幾乎可以等同(比如他們的定義、使用、運行機制都一樣的) ,只是函式的使用方式 更加的 靈活多樣,當一段功能代碼出現多次時 ,編程時 ,就可以將這段功能代碼抽取出來 ,做成函式 ,供呼叫,
2. 函式/方法的定義
|
def 函式名 ([引數名: 引數型別], ...)[[: 回傳值型別] =] { 陳述句 ... //完成某個功能 return 回傳值 } |
- 函式宣告關鍵字為 def (de?nition)
- [引數名: 引數型別], ...:表示函式的輸入(就是引數串列), 可以沒有, 如果有 ,多個引數使用逗號間隔 函式中的陳述句:表示為了實作某一功能代碼塊
- 函式可以有回傳值,也可以沒有 回傳值的形式:
- [: 回傳值型別] = 表示有回傳值 ,并且指定了回傳值的型別
- 沒寫回傳值型別只有等號, 表示回傳值型別 ,使用型別推導
- 空的 ,表示沒有回傳值 ,即使有 return 也不生效
- 如果沒有 return ,默認以執行到最后一行的結果作為回傳值
3. 函式的呼叫機制

4. 函式的遞回呼叫
一個函式/方法在函式/方法體內又呼叫了本身 ,我們稱為遞回呼叫,
例:
|
def test(n:Int){ if (n>2){ test(n ‐1) } else { println(s"n = $n") } } // 輸出n=2 |
5. 注意事項
- 函式的形參串列可以是多個, 如果函式沒有形參 ,呼叫時 可以不帶() ,
- 形參串列和回傳值串列的資料型別可以是值型別和參考型別,
- Scala 中的函式可以根據函式體最后一行代碼自行推斷函式回傳值型別,那么在這種情況下 ,return 關鍵字 可以省略,
- 因為 Scala 可以自行推斷 ,所以在省略 return 關鍵字的場合 ,回傳值型別也可以省略,
- 如果函式明確使用 return 關鍵字 ,那么函式回傳就不能使用自行推斷了,這時要明確寫成 : 回傳型別 = ,當然 如果你什么都不寫 ,即使有 return , 回傳值為(),
- 如果函式明確宣告無回傳值 (宣告 Unit) ,那么函式體中即使使用 return 關鍵字也不會有回傳值,
- 如果明確函式無回傳值或不確定回傳值型別 ,那么回傳值型別可以省略(或宣告為 Any),
- Scala 語法中任何的語法結構都可以嵌套其他語法結構(靈活) ,即 :函式/方法中可以再宣告/定義函式/方法, 類中可以再宣告類,
- Scala 函式的形參 ,在宣告引數時 ,直接賦初始值(默認值) ,這時呼叫函式時 ,如果沒有指定實參 ,則會使用 默認值,如果指定了實參 ,則實參會覆寫默認值,
- 如果存在多個引數 ,每一個引數都可以設定默認值 ,那么這個時候 ,傳遞的引數到底是覆寫默認值 ,還是賦 值給沒有默認值的引數 ,就不確定了(默認按照宣告順序[從左到右]),在這種情況下 ,可以采用帶名引數 , scala 函式的形參默認是 val 的 ,因此不能在函式中進行修改,
- 遞回函式未執行之前是無法推斷出來結果型別 ,在使用時必須有明確的回傳值型別,
- Scala 函式支持可變引數,
6. 程序 (procedure)
將函式的回傳型別為 Unit 的函式稱之為程序(procedure) ,如果明確函式沒有回傳值 ,那么等號可以省略,
例:
|
def f1(name: String): Unit = { println(name + " hello ") } |
如果函式宣告時沒有回傳值型別 ,但是有 = 號 ,可以進行型別推斷最后一行代碼,這時這個函式實際是有回傳值的 ,該函式并不是程序,
7. 惰性函式
惰性計算 (盡可能延遲運算式求值) 是許多函式式編程語言的特性,惰性集合在需要時提供其元素 ,無需預先計算 它們 ,這帶來了一些好處,首先 ,您可以將耗時的計算推遲到絕對需要的時候,其次 ,您可以創造無限個集合 ,只 要它們繼續收到請求 ,就會繼續提供元素,函式的惰性使用讓您能夠得到更高效的代碼,
當函式回傳值被宣告為 lazy 時 ,函式的執行將被推遲 ,直到我們首次對此取值 ,該函式才會執行,這種函式我 們 稱之為惰性函式 ,在 Java的某些框架代碼中稱之為懶加載(延遲加載),
|
def main(args: Array[String]): Unit = { lazy val res = sum(1,2) println(" ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ") println("res=" + res) //當需要使用到 res 時 ,就會真正的開始計算 } def sum(n1:Int,n2:Int): Int = { println("sum 被呼叫 ..") n1 + n2 } |
注意:
- lazy 不能修飾 var 型別的變數
- 不但是 在呼叫函式時 ,加了 lazy ,會導致函式的執行被推遲 ,我們在宣告一個變數時 ,如果給宣告了 lazy , 那么變數值得分配也會推遲, 比如 lazy val i = 10
8. 例外
Scala 提供 try 和 catch 塊來處理例外,try 塊用于包含可能出錯的代碼,catch 塊用于處理 try 塊中發生的例外, 可以根據需要在程式中有任意數量的 try...catch 塊,語法處理上和 Java 類似 ,但是又不盡相同,
例:
|
object ScalaException { def main(args: Array[String]): Unit = { //scala 中去掉所謂的 checked (編譯) 例外 //設計者認為 ,如果程式員編程時 ,認為某段代碼可疑 ,就直接 try 并處理 //說明 //1. 如果代碼可疑 ,使用 try 進行處理 //2. 在 catch 中 ,可以有多個 case ,對可能的例外進行匹配 //3. case ex: Exception => println("例外資訊=" + ex.getMessage) // (1) case 是一個關鍵字 // (2) ex: Exception 例外的種類 // (3) => 表明后的代碼是對例外進行處理 ,如果處理的代碼有多條陳述句可以{}擴起 //4. 在 scala 中把范圍小的例外放在后面 ,語法不會報錯 ,但是不推薦 //5. 如果捕獲例外 ,代碼即使出現例外 ,程式也不會崩潰, try { var res = 10 / 0 } catch { case ex: ArithmeticException => { println("算術例外=" + ex.getMessage) println("111") println("222") } case ex: Exception => println("例外資訊=" + ex.getMessage) } finally { println("finaly 的代碼 ...") } println("程式繼續 ....") } } |
注意:
- 我們將可疑代碼封裝在 try 塊中, 在 try 塊之后使用了一個 catch 處理程式來捕獲例外,如果發生任何例外, catch 處理程式將處理它 ,程式將不會例外終止,
- Scala 的例外的作業機制和 Java 一樣 ,但是 Scala 沒有“checked(編譯期)”例外 ,即 Scala 沒有編譯例外這個 概念 ,例外都是在運行的時候捕獲處理,
- 用 throw 關鍵字 ,拋出一個例外物件,所有例外都是 Throwable 的子型別,throw 運算式是有型別的 ,就是 Nothing ,因為 Nothing 是所有型別的子型別 ,所以 throw 運算式可以用在需要型別的地方
例如:
|
def main(args: Array[String]): Unit = { val res = test() println(res.toString) } def test(): Nothing = { throw new Exception("不對") } |
- 在 Scala 里 ,借用了模式匹配的思想來做例外的匹配 ,因此 ,在 catch 的代碼里 ,是一系列 case 子句來匹配例外,當匹配上后 => 有多條陳述句可以換行寫 ,類 似 java 的 switch case x: 代碼塊..
- 例外捕捉的機制與其他語言中一樣 ,如果有例外發生 ,catch 子句是按次序捕捉的, 因此 ,在 catch 子句中,越具體的例外越要靠前 ,越普遍的例外越靠后 ,如果把越普遍的例外寫在前 ,把具體的例外寫在后 ,在 scala 中也不會報錯(不報錯 ,但是不推薦) ,但這樣是非常不好的編程風格,
- ?nally 子句用于執行不管是正常處理還是有例外發生時都需要執行的步驟 ,一般用于物件的清理作業 ,這點和 Java 一樣,
- Scala 提供了 throws 關鍵字來宣告例外,可以使用方法定義宣告例外, 它向呼叫者函式提供了此方法可能引發此例外的資訊, 它有助于呼叫函式處理并將該代碼包含在 try-catch 塊中 ,以避免程式例外終止,在 scala中 ,可以使用 throws 注釋來宣告例外 例如:
|
def main(args: Array[String]): Unit = { f11() } @throws(classOf[NumberFormatException]) //等同于 NumberFormatException.class def f11() = { "abc".toInt } |
9. 匿名函式
沒有名字的函式就是匿名函式 ,可以通過函式運算式 ,來設定匿名函式,
|
val triple = (x: Double) => 3 * x pritnln(triple) // 型別 println(triple(3)) |
說明 :(x: Double) => 3 * x 就是匿名函式 (x: Double) 是形參串列 , => 是規定語法表示后面是函式體 , 3 * x 就是函式體 ,如果有多行 ,可以 {} 換 行寫.triple 是指向匿名函式的變數,
案例:
|
object NoNameFunction { def main(args: Array[String]): Unit = { //撰寫一個匿名函式 ,可以回傳 2 個整數的和 ,并輸出該匿名函式的型別 //如果我們定義一個函式 ,則變數名字要寫 val f1 = (x1:Int,x2:Int) => { x1 + x2 } println(f1(10, 30)) // 40 println(f1) // <function2> //呼叫 f2 完成一個運算 println(f2(f1,30,40)) // 70 // f = f1 } //方法 ,可以接受一個函式 ,該函式回傳兩個數的差 //這時 ,我們只是寫一個函式的格式(簽名) def f2(f:(Int,Int) => Int, n1:Int,n2:Int): Int = { f(n1,n2) } } |
10. 高階函式
能夠接受函式作為引數的函式 ,叫做高階函式 (higher-order function),可使應用程式更加健壯, 高階函式可 以 回傳一個匿名函式,
10.1. 高階函式的基本使用
|
def test(f: Double => Double, n1: Double) = { f(n1) //呼叫 f 函式 } // def sum(d: Double): Double = { d + d } val res = test(sum, 6.0) println("res=" + res) def minusxy(x: Int) = { (y: Int) => x – y // 函式運算式 , 回傳的是一個匿名函式 } //說明 //minusxy 高階函式 回傳的是 (y: Int) => x – y 匿名函式 //minusxy(3) => 回傳的就是一個具體的匿名函式 (y: Int) => 3 – y
val result3 = minusxy(3)(5) println(result3)
//minusxy(3)(5) => 3 – 5 = ‐2 |
11. 引數(型別)推斷
- 引數型別是可以推斷時 ,可以省略引數型別
- 當傳入的函式 ,只有單個引數時 ,可以省去括號
- 如果變數只在=>右邊只出現一次 ,可以用_來代替
12. 閉包
如果一個函式 ,訪問到了它的外部 (區域) 變數的值 ,那么這個函式和他所處的環境 ,稱為閉包,
|
def minusxy(x: Int) = (y: Int) => x – y //說明 //1. minusxy 回傳了 (y: Int) => x – y 匿名函式 //2. 使用到 x 值 ,x 是它參考到的一個環境變數 //3. 匿名函式和 x 組合成一個整體 ,構成了一個閉包 //4. f 就是一個閉包 val f = minusxy(20) println("f(1)=" + f(1)) // 19 println("f(2)=" + f(2)) // 18 |
12.1 定義
在計算機科學中 ,閉包 (英語:Closure) ,又稱詞法閉包 ( Lexical Closure) 或函式閉包 (function closures) ,是在支持頭等函式的編程語言中實作詞法系結的一種技術,閉包在實作上是一個結構體 ,它存 儲了一個函式 (通常是其入口地址) 和一個關聯的環境 (相當于一個符號查找表) ,環境里是若干對符號和 值的對應關系 ,它既要包括約束變數 (該函式內部系結的符號) ,也要包括自由變數 (在函式外部定義但在 函式內被參考) ,有些函式也可能沒有自由變數,閉包跟函式最大的不同在于 ,當捕捉閉包的時候 ,它的自 由變數會在捕捉時被確定 ,這樣即便脫離了捕捉時的背景關系 ,它也能照常運行,捕捉時對于值的處理可以是 值拷貝 ,也可以是名稱參考 ,這通常由語言設計者決定 ,也可能由用戶自行指定 (如C++) ,
因為外層呼叫結束回傳內層函式后 ,經過堆疊調整(比如在C中主調或者被調清理) ,外層函式的引數已經被釋放了 ,所以內層是獲取不到外層的函式引數的,為了能夠將環境 (函式中用到的并非該函式引數的變數和他 們的值) 保存下來 (需要考慮釋放問題 ,可以通過GC可以通過物件生命周期控制 ,GC是一個常見選擇) ,這 時會將執行的環境打一個包保存到堆里面,
13. 函式柯里化 (Currying)
將一個引數串列的多個引數 ,變成多個引數串列的程序,也就是將普通多引數函式變成高階函式的程序,
13.1 定義
在計算機科學中 ,柯里化 (英語:Currying) ,又譯為卡瑞化或加里化 ,是把接受多個引數的函式變換成接 受一個單一引數 (最初函式的第一個引數) 的函式 ,并且回傳接受余下的引數而且回傳結果的新函式的技 術,在直覺上 ,柯里化聲稱“如果你固定某些引數 ,你將得到接受余下引數的一個函式”,柯里化是一種處理 函式中附有多個引數的方法 ,并在只允許單一引數的框架中使用這些函式,
13.2 scala中的柯里化函式
|
// Currying def add(a: Int)(b: Int): Int = a + b println(add(4)(3)) val addFour = add(4) _ // val addFour: Int => int = add(4) println(addFour(3)) |
14. 控制抽象
值呼叫:按值傳遞引數 ,計算值后再傳遞,多數語言中一般函式呼叫都是這個方式 ,C++還存在參考傳遞,
名呼叫:按名稱傳遞引數 ,直接用實參替換函式中使用形參的地方,能想到的只有C語言中的帶參宏函式 ,其 實并不是函式呼叫 ,預處理時直接替換, 例子:
|
// pass by value def f0(a: Int): Unit = { println("a: " + a) println("a: " + a) } f0(10) // pass by name, argument can be a code block that return to Int def f1(a: => Int): Unit = { println("a: " + a) println("a: " + a) } def f2(): Int = { println("call f2()") 10 } f1(10) f1(f2()) // pass by name, just replace a with f2(), then will call f2() twice f1({ println("code block") // print twice 30 }) |
應用:使用傳名引數實作一個函式相當于while的功能,
|
// built ‐in while var n = 10 while (n >= 1) { print(s"$n ") n-= 1 } println() // application: self ‐defined while, implement a function just like while keyword def myWhile(condition: => Boolean): (=> Unit) => Unit = { def doLoop(op: => Unit): Unit = { if (condition) { op myWhile(condition)(op) } } doLoop _ } n= 10 myWhile (n >= 1) { print(s"$n ") n ‐= 1 } println() // simplfy def myWhile2(condition: => Boolean): (=> Unit) => Unit = { op => { if (condition) { op myWhile2(condition)(op) } } } n= 10 myWhile (n >= 1) { print(s"$n ") n ‐= 1 } println() // use currying def myWhile3(condition: => Boolean)(op: => Unit): Unit = { if (condition) { op myWhile3(condition)(op) } } n= 10 myWhile3 (n >= 1) { print(s"$n ") n ‐= 1 } println() |
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/551891.html
標籤:Scala
上一篇:Django筆記三十八之發送郵件
下一篇:返回列表
