轨道
/
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,全部免费。