主頁 > 後端開發 > Scala學習總結(一)

Scala學習總結(一)

2023-05-09 07:36:42 後端開發

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筆記三十八之發送郵件

下一篇:返回列表

標籤雲
其他(158614) Python(38123) JavaScript(25405) Java(18024) C(15222) 區塊鏈(8262) C#(7972) AI(7469) 爪哇(7425) MySQL(7171) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5871) 数组(5741) R(5409) Linux(5336) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4567) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2432) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1965) Web開發(1951) HtmlCss(1932) python-3.x(1918) 弹簧靴(1913) C++(1912) xml(1889) PostgreSQL(1874) .NETCore(1857) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Scala學習總結(一)

    Scala學習總結 一、Scala簡介 Scala特點: Scala 是一門多范式 (multi-paradigm) 的編程語言 ,設計初衷是要集成面向物件編程和函式式編程的各種 特性。 Scala 是一門以 java 虛擬機 (JVM) 為運行環境的編程語言 ,Scala 源代碼(.scala)會 ......

    uj5u.com 2023-05-09 07:36:42 more
  • Django筆記三十八之發送郵件

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記三十八之發送郵件 這一篇筆記介紹如何在 Django 中發送郵件。 在 Python 中,提供了 smtplib 的郵件模塊,而 Django 在這個基礎上對其進行了封裝,我們可以通過 django.core.mail 來呼叫。 以下是本 ......

    uj5u.com 2023-05-09 07:36:08 more
  • python快速直白入門(半新手向,老手復習向)

    主用python做專案有一段時間,這次簡單總結學習下。為后面的專案撰寫,進行一次基礎知識的查缺補漏、 1、變數名和資料型別 """ 變數名,只能由" 數字、大小寫字母、_ " 組成,且不能以數字開頭 """ # 整數 int # hashable,不可變物件 a = 5 # 浮點數 float # ......

    uj5u.com 2023-05-09 07:30:54 more
  • Python第三方庫安裝教程、什么是第三方庫

    Python有一個全球社區:https://pypi.org/,在這里我們可以搜索任何主題的Python第三方庫。PyPI全稱是Python Package Index,指的是Python包的索引,它由PSF(Python Software Foundation)來維護,并且展示全球Python計算 ......

    uj5u.com 2023-05-09 07:30:48 more
  • Windows 安裝 pycrypto 常見問題解決

    首先pycrypto 專案已經不在更新了,自己開發可以使用cryptography、pycryptodome https://pypi.org/project/cryptography/https://pypi.org/project/pycryptodome/ 關于python使用Crypto.C ......

    uj5u.com 2023-05-09 07:28:49 more
  • 幾種常見的Python資料結構

    摘要:本文主要為大家講解在Python開發中常見的幾種資料結構。 本文分享自華為云社區《Python的常見資料結構》,作者: timerring 。 資料結構和序列 元組 元組是一個固定長度,不可改變的Python序列物件。創建元組的最簡單方式,是用逗號分隔一列值: In [1]: tup = 4, ......

    uj5u.com 2023-05-09 07:22:56 more
  • Spring 注解

    @SpringBootApplication 申明讓spring boot自動給程式進行必要的配置,這個配置等同于: @Configuration ,@EnableAutoConfiguration 和 @ComponentScan 三個配置。 @RequestMapping 提供路由資訊,負責UR ......

    uj5u.com 2023-05-09 07:19:04 more
  • ChatGPT 對接微信公眾號技術方案實作!

    如假期前小傅哥的計劃一樣,這個假期開啟了新的技術專案《ChatGPT 微服務應用體系構建》教程;從搭建環境、開發chatgpt-sdk-java、對接公眾號、封裝api,直至假期最后一天,完成了微信公眾號的對接,可以與 ChatGPT 對話聊天啦!。 ......

    uj5u.com 2023-05-08 08:35:31 more
  • ChatGPT 對接微信公眾號技術方案實作!

    如假期前小傅哥的計劃一樣,這個假期開啟了新的技術專案《ChatGPT 微服務應用體系構建》教程;從搭建環境、開發chatgpt-sdk-java、對接公眾號、封裝api,直至假期最后一天,完成了微信公眾號的對接,可以與 ChatGPT 對話聊天啦!。 ......

    uj5u.com 2023-05-08 08:34:29 more
  • Django筆記三十六之單元測驗匯總介紹

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記三十六之單元測驗匯總介紹 Django 的單元測驗使用了 Python 的標準庫:unittest。 在我們創建的每一個 application 下面都有一個 tests.py 檔案,我們通過繼承 django.test.TestCase ......

    uj5u.com 2023-05-07 07:50:37 more