1. 程式人生 > 其它 >Scala_05_方法和函式

Scala_05_方法和函式

章節目標

  1. 掌握方法的格式和用法
  2. 掌握函式的格式和用法
  3. 掌握九九乘法表案例

1. 方法

1.1 概述

實際開發中, 我們需要編寫大量的邏輯程式碼, 這就勢必會涉及到重複的需求. 例如: 求10和20的最大值, 求11和22的最大值, 像這樣的需求, 用來進行比較的邏輯程式碼需要編寫兩次, 而如果把比較的邏輯程式碼放到方法中, 只需要編寫一次就可以了, 這就是方法. scala中的方法和Java方法類似, 但scala與Java定義方法的語法是不一樣的。

1.2 語法格式

def 方法名(引數名:引數型別, 引數名:引數型別) : [return type] = {
    //方法體
}

注意:

  • 引數列表的引數型別不能省略
  • 返回值型別可以省略,由scala編譯器自動推斷
  • 返回值可以不寫return,預設就是{}塊表示式的值

1.3 示例

**需求: **

  1. 定義一個方法getMax,用來獲取兩個整型數字的最大值, 並返回結果(最大值).
  2. 呼叫該方法獲取最大值, 並將結果列印到控制檯上.

參考程式碼

  • 方式一: 標準寫法
//1. 定義方法, 用來獲取兩個整數的最大值.
def getMax(a:Int, b:Int): Int = {
    return if(a > b) a else b
}
//2. 呼叫方法, 獲取最大值.
val max = getMax(10, 20)
//3. 列印結果.
println("max: " + max)
  • 方式二: 優化版
//1. 定義方法, 用來獲取兩個整數的最大值.
def getMax(a:Int, b:Int) = if(a > b) a else b
//2. 呼叫方法, 獲取最大值.
val max = getMax(22, 11)
//3. 列印結果.
println("max: " + max)

1.4 返回值型別推斷

scala定義方法可以省略返回值的資料型別,由scala自動推斷返回值型別。這樣方法定義後更加簡潔。

注意: 定義遞迴方法,不能省略返回值型別

示例

定義遞迴方法, 求5的階乘.

步驟

  1. 定義方法factorial, 用來計算某個數字的階乘

    規律: 1的階乘等於1, 其他數字的階乘為: n! = n * (n - 1)!

  2. 呼叫方法, 獲取5的階乘, 並將結果列印到控制檯上.

參考程式碼

//1. 定義方法factorial, 用來計算某個數字的階乘
def factorial(n:Int):Int = if(n == 1) 1 else n * factorial(n - 1)
//2. 呼叫方法, 獲取5的階乘.
val result = factorial(5)
//3. 將結果列印到控制檯上.
println("result: " + result)

1.5 惰性方法

當記錄方法返回值的變數被宣告為lazy時, 方法的執行將被推遲, 直到我們首次使用該值時, 方法才會執行, 像這樣的方法, 就叫: 惰性方法.

注意:

  1. Java中並沒有提供原生態的"惰性"技術, 但是可以通過特定的程式碼結構實現, 這種結構被稱之為: 懶載入(也叫延遲載入)
  2. lazy不能修飾var型別的變數.

使用場景:

  1. 開啟資料庫連線

    由於表示式執行代價昂貴, 因此我們希望能推遲該操作, 直到我們確實需要表示式結果值時才執行它

  2. 提升某些特定模組的啟動時間.

    為了縮短模組的啟動時間, 可以將當前不需要的某些工作推遲執行

  3. 確保物件中的某些欄位能優先初始化

    為了確保物件中的某些欄位能優先初始化, 我們需要對其他欄位進行惰性化處理

需求

定義一個方法用來獲取兩個整數和, 通過"惰性"技術呼叫該方法, 然後列印結果.

參考程式碼

//1. 定義方法, 用來獲取兩個整數和
def getSum(a:Int, b:Int) = {
    println("getSum方法被執行了...")
    a + b
}
//2. 通過"惰性"方式呼叫該方法.
lazy val sum = getSum(1, 2)		//此時我們發現getSum方法並沒有執行, 說明它的執行被推遲了.

//3. 列印結果, 並觀察
println("sum: " + sum) 			//列印結果為sum: 3, 說明首次使用方法返回值時, 方法才會載入執行.

1.6 方法引數

scala中的方法引數,使用比較靈活。它支援以下幾種型別的引數:

  • 預設引數
  • 帶名引數
  • 變長引數

1.6.1 預設引數

在定義方法時可以給引數定義一個預設值。

示例

  1. 定義一個計算兩個整數和的方法,這兩個值分別預設為10和20
  2. 呼叫該方法,不傳任何引數

參考程式碼

//1. 定義一個方法, 用來獲取兩個整數的和
// x,y的預設值分別為10和20
def getSum(x:Int = 10, y:Int = 20) = x + y
//2. 通過預設引數的形式, 呼叫方法
val sum = getSum()
//3. 列印結果
println("sum: " + sum)

1.6.2 帶名引數

在呼叫方法時,可以指定引數的名稱來進行呼叫。

示例

  1. 定義一個計算兩個整數和的方法,這兩個值分別預設為10和20
  2. 呼叫該方法,只設置第一個引數的值

參考程式碼

//1. 定義一個方法, 用來獲取兩個整數的和
def getSum(x:Int = 10, y:Int = 20) = x + y
//2. 通過預設引數的形式, 呼叫方法
val sum = getSum(x=1)
//3. 列印結果
println("sum: " + sum)

1.6.3 變長引數

如果方法的引數是不固定的,可以將該方法的引數定義成變長引數。

語法格式:

def 方法名(引數名:引數型別*):返回值型別 = {
    //方法體
}

注意:

  1. 在引數型別後面加一個*號,表示引數可以是0個或者多個
  2. 一個方法有且只能有一個變長引數, 並且變長引數要放到引數列表的最後邊.

**示例一: **

  1. 定義一個計算若干個值相加的方法
  2. 呼叫方法,傳入以下資料:1,2,3,4,5

參考程式碼

//1. 定義一個計算若干個值相加的方法
def getSum(a:Int*) = a.sum
//2. 呼叫方法,傳入一些整數, 並獲取它們的和
val sum = getSum(1,2,3,4,5)
//3. 列印結果
println("sum: " + sum)

1.7 方法呼叫方式

在scala中,有以下幾種方法呼叫方式:

  • 字尾呼叫法
  • 中綴呼叫法
  • 花括號呼叫法
  • 無括號呼叫法

注意: 在編寫spark、flink程式時,會經常使用到這些方法呼叫方式。

1.7.1 字尾呼叫法

這種方法與Java沒有區別, 非常簡單.

語法

物件名.方法名(引數)

示例

使用字尾法呼叫Math.abs, 用來求絕對值

參考程式碼

//字尾呼叫法
Math.abs(-1)	//結果為1

1.7.2 中綴呼叫法

語法

物件名 方法名 引數

例如:1 to 10

注意: 如果有多個引數,使用括號括起來

示例

使用中綴法呼叫Math.abs, 用來求絕對值

//中綴呼叫法
Math abs -1		//結果為1

擴充套件: 操作符即方法

來看一個表示式, 大家覺得這個表示式像不像方法呼叫?

1 + 1

在scala中,+ - * / %等這些操作符和Java一樣,但在scala中,

  • 所有的操作符都是方法
  • 操作符是一個方法名字是符號的方法

1.7.3 花括號呼叫法

語法

Math.abs{ 
    // 表示式1
    // 表示式2
}

注意: 方法只有一個引數,才能使用花括號呼叫法

示例

使用花括號呼叫法Math.abs求絕對值

參考程式碼

//花括號呼叫法
Math.abs{-10}	//結果為: 10

1.7.4 無括號呼叫法

如果方法沒有引數,可以省略方法名後面的括號

示例

  • 定義一個無引數的方法,列印"Hello, Scala!"
  • 使用無括號呼叫法呼叫該方法

參考程式碼

//1. 定義一個無引數的方法,列印"Hello, Scala!"
def sayHello() = println("Hello, Scala!")
//2. 呼叫方法
sayHello

注意:

  1. 在Scala中, 如果方法的返回值型別是Unit型別, 這樣的方法稱之為過程(procedure)
  2. 過程的等號(=)可以省略不寫. 例如:
def sayHello() = println("Hello, Scala!")   
//可以改寫為
def sayHello() { println("Hello, Scala!") }	 //注意: 這個花括號{}不能省略

2. 函式

scala支援函數語言程式設計,將來編寫Spark/Flink程式會大量使用到函式, 目前, 我們先對函式做一個簡單入門, 在後續的學習過程中, 我們會逐步重點講解函式的用法.

2.1 定義函式

語法

val 函式變數名 = (引數名:引數型別, 引數名:引數型別....) => 函式體

注意:

  • 在Scala中, 函式是一個物件(變數)
  • 類似於方法,函式也有引數列表和返回值
  • 函式定義不需要使用def定義
  • 無需指定返回值型別

2.2 示例

rgb(251, 249, 253)

**需求: **

  1. 定義一個計算兩個整數和的函式
  2. 呼叫該函式

參考程式碼

//1. 定義一個用來計算兩個整數和的函式, 並將其賦值給變數sum
val getSum = (x:Int, y:Int) => x + y
//2. 呼叫函式.
val result = getSum(1,2)
//3. 列印結果
println("result: " + result)

2.3 方法和函式的區別

在Java中, 方法和函式之間沒有任何區別, 只是叫法不同. 但是在Scala中, 函式和方法就有區別了, 具體如下:

  • 方法是隸屬於類或者物件的,在執行時,它是載入到JVM的方法區中
  • 可以將函式物件賦值給一個變數,在執行時,它是載入到JVM的堆記憶體中
  • 函式是一個物件,繼承自FunctionN,函式物件有apply,curried,toString,tupled這些方法。方法則沒有

結論: 在Scala中, 函式是物件, 而方法是屬於物件的, 所以可以理解為: 方法歸屬於函式.

示例

演示方法無法賦值給變數

//1. 定義方法
def add(x:Int,y:Int)= x + y

//2. 嘗試將方法賦值給變數.
//val a = add(1, 2)		//不要這樣寫, 這樣寫是在"呼叫方法", 而不是把方法賦值給變數
val a = add

//3. 上述程式碼會報錯
<console>:12: error: missing argument list for method add
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `add _` or `add(_,_)` instead of `add`.
       val a = add

2.4 方法轉換為函式

有時候需要將方法轉換為函式. 例如: 作為變數傳遞,就需要將方法轉換為函式

格式

val 變數名 = 方法名 _		//格式為: 方法名 + 空格 + 下劃線

注意: 使用 _即可將方法轉換為函式

示例

  1. 定義一個方法用來計算兩個整數和
  2. 將該方法轉換為一個函式,並賦值給變數

參考程式碼

//1. 定義一個方法用來計算兩個整數和
def add(x:Int, y:Int)= x + y
//2. 將該方法轉換為一個函式,並賦值給變數
val a = add _
//3. 呼叫函式, 用來獲取兩個整數的和.
val result = a(1, 2)
//4. 列印結果
println("result: " + result)

3. 案例: 列印nn乘法表

3.1 需求

定義方法實現, 根據使用者錄入的整數, 列印對應的乘法表。

例如: 使用者錄入5,則列印55乘法表,使用者錄入9,則列印99乘法表。

3.2 目的

  1. 考察鍵盤錄入和方法, 函式的綜合運用.
  2. 體會方法和函式的不同.

3.3 步驟

  1. 定義方法(或者函式), 接收一個整型引數.
  2. 通過for迴圈巢狀實現, 根據傳入的整數, 列印對應的乘法表.
  3. 呼叫方法(函式), 輸出結果.

3.4 參考程式碼

  • 方式一: 通過方法實現
//1. 定義一個方法, 接收一個整型引數.
def printMT(n:Int) = {   //Multiplication Table(乘法表)
    //2. 通過for迴圈巢狀實現, 根據傳入的整數, 列印對應的乘法表.
    for(i <- 1 to n; j <- 1 to i) {
    	print(s"${j} * ${i} = ${j * i}\t"); 
        if(j==i) println()
    }
}
//3. 呼叫方法
printMT(5)

//優化版: 上述定義方法的程式碼可以合併為一行(目前只要能看懂即可)
/*def printMT(n:Int) = for(i <- 1 to n; j <- 1 to i) print(s"${j} * ${i} = ${j * i}" + (if(j==i) "\r\n" else "\t"))*/
  • 方式二: 通過函式實現
//1. 定義一個函式, 接收一個整型引數.
val printMT = (n:Int) => {
    //2. 通過for迴圈巢狀實現, 根據傳入的整數, 列印對應的乘法表.
    for(i <- 1 to n; j <- 1 to i) {
    	print(s"${j} * ${i} = ${j * i}\t"); 
        if(j==i) println()
    }
}
//3. 呼叫函式
printMT(9)

//優化版: 上述定義函式的程式碼可以合併為一行(目前只要能看懂即可)
/*val printMT = (n:Int) => for(i <- 1 to n; j <- 1 to i) print(s"${j} * ${i} = ${j * i}" + (if(j==i) "\r\n" else "\t"))*/