學習軌道
/
Swift
Swift
/
練習
/
特務 Double-Null0111
特務 Double-Null0111

特務 Double-Null0111

學習練習

簡介

函式型別

如果把 Swift 函式簽章裡的所有名稱和標籤都拿掉,剩下的就是這個函式的型別。

func shoppingList(item: String, quantity: Int) -> String {
  "You need to buy \(quantity) \(item)."
}
// shoppingList: (String, Int) -> String

func repeater(_ phrase: String, times: Int) -> String {
  var result = ""
  for _ in 1...times {
    result += phrase
  }
  return result
}
// repeater: (String, Int) -> String

func printString(line: String) {
  print(line)
}
// printString: (String) -> ()

注意,前兩個函式的型別都是 (String, Int) -> String,即使它們的簽章不同,運作方式也大不相同。它們都接受一個String和一個Int作為輸入,並回傳一個String。而從第三個函式的型別可以看出,雖然它的函式主體看起來沒有回傳任何東西,其實它隱含地回傳了(),也就是型別Void唯一的值。

這些函式型別可以像 Swift 中任何其他型別一樣使用,例如可以用來宣告變數或常數。而且因為函式在 Swift 中也是一種值,所以可以把現成的函式指定給這些常數或變數:

var stringAndIntToString: (String, Int) -> String

stringAndIntToString = shoppingList
stringAndIntToString("carrots", 3)
// => "You need to buy 3 carrots."

和其他變數或常數一樣,你指定給它的值必須是正確的型別:

stringAndIntToString = printShoppingList
// Error: Cannot assign value of type '(String) -> ()' to type '(String, Int) -> String'

函式型別作為參數型別

由於函式型別可以用在任何其他型別能用的地方,所以它當然也能當作參數型別,傳入其他函式,並在那些函式裡被呼叫。

func apply3(to str: String, function f: (String, Int) -> String) -> String {
  f(str,3)
}
// apply3: ([String], (String, Int) -> String) -> [String]

apply3(to: "eggs", function: shoppingList)
// => "You need to buy 3 eggs."

apply3(to: "eggs", function: repeater)
// => "eggseggseggs"

函式型別作為回傳型別

同樣地,函式型別也可以當作函式的回傳型別。換句話說,你可以撰寫會建立並回傳其他函式的函式。建立這些函式時,被回傳的函式可以使用傳入的參數,或是父函式內部建立的區域變數。

func makeAdder(base: Int) -> (Int) -> Int {
  func adder(_ i: Int) -> Int {
    base + i
  }
  return adder
}
// makeAdder: (Int) -> (Int) -> Int

let add10 = makeAdder(base: 10)
// add10: (Int) -> Int

let subtract20 = makeAdder(base: -20)
// subtract10: (Int) -> Int

add10(5)
// => 15

subtract20(5)
// => -15

說明

你好,Double-Null0111 探員。

你的任務(如果你選擇接受的話),是撰寫兩件工具,幫你潛入 UMBRA(United Minions Being Really Awful)總部,取回他們最新那樁糟糕計畫的計畫書。執行這項任務會用到兩件工具。

1. 用密碼保護取回的機密計畫

一旦拿到機密計畫,你就得好好保護它,讓只有知道密碼的人才能取回。

為了做到這一點,你需要實作protectSecret(_ secret: String, withPassword password: String) -> (String) -> String這個函式。 這個函式會回傳另一個函式,它接受各種可能的密碼字串作為輸入。 如果輸入的密碼正確,新的函式就會回傳藏起來的機密;但如果密碼錯誤,這個函式會回傳 "Sorry. No hidden secrets here."。

let secretFunction = protectSecret("Steal the world's supply of french fries!", withPassword: "5up3r53cr37")

secretFunction("Open sesame")
// Returns "Sorry. No hidden secrets here."

secretFunction("5up3r53cr37")
// Returns "Steal the world's supply of french fries!"

2. 產生一組能打開他們保險箱的密碼組合

我們得知,UMBRA 所有保險箱的密碼組合,都是以所在房間的號碼為依據產生的。 密碼組合的產生方式,是對房間號碼反覆套用一個特別的函式。 所以密碼組合的第一個數字,來自把函式套用在房間號碼上;第二個數字,來自把函式套用在第一個數字上;第三個數字,則來自把函式套用在第二個數字上。

不過,UMBRA 會定期更換用來產生密碼組合的函式,所以你的工具需要同時接收房間號碼和對應的函式,才能產生密碼組合。

請實作generateCombination(forRoom room: Int, usingFunction f: (Int) -> Int) -> (Int, Int, Int)這個函式,來產生你需要的密碼組合。

func makeCombo(_ x: Int) -> Int {
  (x * 13) % 256
}

generateCombination(forRoom: 227, usingFunction: makeCombo)
// Returns (135, 219, 31)
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Swift Exercism

準備好開始 特務 Double-Null0111 了嗎?

註冊 Exercism,透過 35 個概念116 個練習 和真人引導來學習並精通 Swift,全部免費。