Scala學習總結(一)

来源:https://www.cnblogs.com/zb-7071/archive/2023/05/08/17381783.html
-Advertisement-
Play Games

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


Scala學習總結

一、Scala簡介

Scala特點:

  •   Scala 是一門多範式 (multi-paradigm) 的編程語言 ,設計初衷是要集成面向對象編程和函數式編程的各種 特性。
  •   Scala 是一門以 java 虛擬機 (JVM) 為運行環境的編程語言 ,Scala 源代碼(.scala)會被編譯成 Java 位元組碼(.class) ,然後運行於 JVM 之上 ,並可以調用現有的 Java 類庫 ,實現兩種語言的無縫對接。 強類型語言
  •   簡潔高效 (各種語法糖)
  •   源於java ,與java對比學習 ,更易掌握

二、變數

1. 基本語法

  先聲明再使用:

val/var 變數名 [:變數類型] = 變數值

  註意: 變數類型可以省略 ,變數類型確定後就無法改變 (強類型語言) var修飾的變數可以改變 ,val修飾的變數 不可變。 val是線程安全的 ,效率更高 ,val修飾的變數在編譯後 ,等同於加上了final 。

      變數聲明時需要初始值。

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 (definition)
  • [參數名: 參數類型], ...:表示函數的輸入(就是參數列表), 可以沒有。 如果有 ,多個參數使用逗號間隔 函數中的語句:表示為了實現某一功能代碼塊
  • 函數可以有返回值,也可以沒有 返回值的形式:
    • [: 返回值類型] =   表示有返回值 ,並且指定了返回值的類型
    • 沒寫返回值類型只有等號, 表示返回值類型 ,使用類型推導
    • 空的 ,表示沒有返回值 ,即使有 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 中也不會報錯(不報錯 ,但是不推薦) ,但這樣是非常不好的編程風格。
  • finally 子句用於執行不管是正常處理還是有異常發生時都需要執行的步驟 ,一般用於對象的清理工作 ,這點和 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()

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 不要跳過這部分知識,對瞭解 NodeManager 本地目錄結構,和熟悉 Container 啟動流程有幫助。 一、分散式緩存介紹 主要作用就是將用戶應用程式執行時,所需的外部文件資源下載緩存到各個節點。 YARN 分散式緩存工作流程如下: 客戶端將應用程式所需的文件資源 (外部字典、JAR 包、二 ...
  • ​ 本文分享一個給力的Java後端面試題網站:面試梯。 網址:https://offer.skyofit.com 這套題真實、高頻、全面、有詳細答案、保你穩過面試,讓你成為offer收割機。題目包括:Java基礎、多線程、JVM、資料庫、Redis、Shiro、Spring、SpringBoot、M ...
  • 3.1一個簡單的Java語言程式 這是程式雖然很簡單,但是所有的Java程式都具有這種結構,因此還是值得花一些時間來研究的。首先,Java區分大小寫。如果出現了大小寫拼寫錯誤(例如:將main拼寫成Main),程式將無法運行。 下麵逐行的查看這段源代碼。關鍵字pubilc稱為訪問修飾符(access ...
  • 基於Java的簡單圖書館管理系統實現,圖書租借管理系統,租借系統,springboot圖書館管理系統,大學圖書管理系統,圖書借閱系統,圖書館借閱歸還系統。 ...
  • 主用python做項目有一段時間,這次簡單總結學習下。為後面的項目編寫,進行一次基礎知識的查缺補漏、 1、變數名和數據類型 """ 變數名,只能由" 數字、大小寫字母、_ " 組成,且不能以數字開頭 """ # 整數 int # hashable,不可變對象 a = 5 # 浮點數 float # ...
  • Python有一個全球社區:https://pypi.org/,在這裡我們可以搜索任何主題的Python第三方庫。PyPI全稱是Python Package Index,指的是Python包的索引,它由PSF(Python Software Foundation)來維護,並且展示全球Python計算 ...
  • 聲明 本文章中所有內容僅供學習交流使用,不用於其他任何目的,不提供完整代碼,抓包內容、敏感網址、數據介面等均已做脫敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切後果均與作者無關! 本文章未經許可禁止轉載,禁止任何修改後二次傳播,擅自使用本文講解的技術而導致的任何意外,作者均不負責,若有侵權, ...
  • ElasticSearch 分詞器 作者: 博學谷狂野架構師 GitHub:GitHub地址 (有我精心準備的130本電子書PDF) 只分享乾貨、不吹水,讓我們一起加油!😄 概述 分詞器的主要作用將用戶輸入的一段文本,按照一定邏輯,分析成多個詞語的一種工具 什麼是分詞器 顧名思義,文本分析就是把全 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...