Scala 3 — 書籍

建立回傳函式的函式

語言

感謝 Scala 的一致性,撰寫回傳函式的函式與您在先前各節中所見的內容類似。例如,想像一下您想要撰寫回傳函式的 greet 函式。我們再次從問題陳述開始

我想建立回傳函式的 greet 函式。該函式將接收字串參數,並使用 println 進行列印。為了簡化這個第一個範例,greet 函式不會接收任何輸入參數;它只會建立一個函式並回傳它。

根據該陳述,您可以開始建立 greet 函式。您知道它會是一個函式

def greet()

您也知道這個方法會回傳一個函式,這個函式 (a) 接收一個 String 參數,以及 (b) 使用 println 函式列印該字串。因此,該函式的類型為 String => Unit

def greet(): String => Unit = ???
           ----------------

現在您只需要一個方法主體。您知道該方法需要回傳一個函式,而該函式會接收一個 String 並列印它。這個匿名函式符合該描述

(name: String) => println(s"Hello, $name")

現在您只要從該方法回傳該函式即可

// a method that returns a function
def greet(): String => Unit = 
  (name: String) => println(s"Hello, $name")

由於這個方法會回傳一個函式,因此您可以透過呼叫 greet() 來取得該函式。這是可以在 REPL 中執行的良好步驟,因為它會驗證新函式的類型

scala> val greetFunction = greet()
val greetFunction: String => Unit = Lambda....
    -----------------------------

現在您可以呼叫 greetFunction

greetFunction("Joe")   // prints "Hello, Joe"

恭喜您,您剛剛建立了一個會回傳函式的函式,然後執行該函式。

改善方法

如果您能傳入一個問候語,我們的函式會更有用,因此我們來這麼做。您只需要將問候語作為參數傳遞給 greet 方法,並在 println 內部的字串中使用它

def greet(theGreeting: String): String => Unit = 
  (name: String) => println(s"$theGreeting, $name")

現在當您呼叫您的方法時,這個程序會更靈活,因為您可以變更問候語。當您從這個方法建立一個函式時,它看起來會像這樣

scala> val sayHello = greet("Hello")
val sayHello: String => Unit = Lambda.....
    ------------------------

REPL 類型簽章輸出顯示 sayHello 是接收 String 輸入參數並回傳 Unit (無) 的函式。因此,現在當您給予 sayHello 一個 String 時,它會列印問候語

sayHello("Joe")   // prints "Hello, Joe"

您也可以變更問候語來建立新的函式,視需要而定

val sayCiao = greet("Ciao")
val sayHola = greet("Hola")

sayCiao("Isabella")   // prints "Ciao, Isabella"
sayHola("Carlos")     // prints "Hola, Carlos"

更貼近真實世界的範例

當您的方法回傳許多可能的函式之一時,這個技術會更有用,例如回傳自訂函式的工廠。

例如,假設您想要撰寫一個方法,這個方法會回傳使用不同語言向人們問候的函式。我們會將此限制為使用英文或法文問候的函式,這取決於傳遞給該方法的參數。

您首先知道您想要建立一個方法,這個方法 (a) 接收「目標語言」作為輸入,以及 (b) 回傳一個函式作為其結果。此外,由於該函式會列印給予它的字串,因此您知道它的類型為 String => Unit。有了這些資訊,您可以撰寫方法簽章

def createGreetingFunction(desiredLanguage: String): String => Unit = ???

接下來,由於您知道您會回傳的可能函式會接收一個字串並列印它,因此您可以為英文和法文撰寫兩個匿名函式

(name: String) => println(s"Hello, $name")
(name: String) => println(s"Bonjour, $name")

在方法內部,如果您給這些匿名函式一些名稱,可能會更易於閱讀,因此我們將它們指定給兩個變數

val englishGreeting = (name: String) => println(s"Hello, $name")
val frenchGreeting = (name: String) => println(s"Bonjour, $name")

現在您所需要做的就是 (a) 如果 desiredLanguage 是英文,則傳回 englishGreeting,以及 (b) 如果 desiredLanguage 是法文,則傳回 frenchGreeting。一種做法是使用 match 表達式

def createGreetingFunction(desiredLanguage: String): String => Unit = {
  val englishGreeting = (name: String) => println(s"Hello, $name")
  val frenchGreeting = (name: String) => println(s"Bonjour, $name")
  desiredLanguage match {
    case "english" => englishGreeting
    case "french" => frenchGreeting
  }
}
def createGreetingFunction(desiredLanguage: String): String => Unit =
  val englishGreeting = (name: String) => println(s"Hello, $name")
  val frenchGreeting = (name: String) => println(s"Bonjour, $name")
  desiredLanguage match
    case "english" => englishGreeting
    case "french" => frenchGreeting

這就是最後的方法。請注意,從方法傳回函式值與傳回字串或整數值並無不同。

這是 createGreetingFunction 建立法文問候函式的方式

val greetInFrench = createGreetingFunction("french")
greetInFrench("Jonathan")   // prints "Bonjour, Jonathan"

這是它建立英文問候函式的方式

val greetInEnglish = createGreetingFunction("english")
greetInEnglish("Joe")   // prints "Hello, Joe"

如果您對該程式碼感到自在,恭喜您,現在您知道如何撰寫傳回函式的函式。

此頁面的貢獻者