導入
以下のような問題を想定します。
func randomDirection() -> String{
["前","右","後","左"].randomElement()!
}
と定義されたrandomDirection
を引数として、前であれば右、右であれば後といったように時計回りの方向を文字列で返す関数を作成せよ。
考察
🤔「switch使ったろ!」
func nextDirection(direction: String) -> String {
switch direction {
case "前":
return "右"
case "右":
return "後"
case "後":
return "左"
case "左":
return "前"
}
}
👮♂️「Switch must be exhaustive(網羅できてないよ)」
😅「」
😁「せや!case "左"をdefaultとして扱ったろ!」
func nextDirection() -> String {
switch randomDirection() {
case "前":
return "右"
case "右":
return "後"
case "後":
return "左"
default:
return "前"
}
}
🕵️♂️「値を取得する関数がrandomDirection()
のように簡単な関数であるとは限りません。それ以外の場合と一つの値を同じ処理にしてしまうのはバグの発見を遅らせてしまうため、いい実装とは言えません。」
😭「」
😁「せや!defaultの時はnilとしたろ!」
func nextDirection(direction: String) -> String {
switch direction {
case "前":
return "右"
case "右":
return "後"
case "後":
return "左"
default:
return "前"
default:
return nil
}
}
👮♂️「'nil' is incompatible with return type 'String'(返り値はStringなんだがw)」
😁「返り値String?
にしたろ!」
🕵️♂️「問題はString
で返す関数を作成することです。」
😭「『switchの分岐を全て網羅すること』と『型にオプショナルを加えないこと』とが両立できない」
列挙型(enum)を使う
🕵️♂️「列挙型を使うといいよ」
🧐「なるほど」
enum Direction {
case 前
case 右
case 後
case 左
}
🧐「とすればDirection
型は4つのどれかであるといえるのか。これらは変数の名前だから値を持たせる必要があるな。」
enum Direction: String {
case 前 = "前"
case 右 = "右"
case 後 = "後"
case 左 = "左"
}
🧐「rawValue
を使えば値から型を作れるのか」
let direction = Direction(rawValue: direction)!
😁「これを使って関数を書き換えられる!」
func nextDirection(direction: String) -> String {
let direction = Direction(rawValue: direction)!
switch direction {
case .前:
return "右"
case .右:
return "後"
case .後:
return "左"
case .左:
return "前"
}
}
👮♂️「」
省略する
🕵️♂️「caseとvalueが一致してたら省略できます。」
enum Direction: String {
case 前
case 右
case 後
case 左
}
😍「すごい」
工夫する
🕵️♂️「CaseIterable
を使えば列挙型の全てを取得するallCases
が使えます。」
enum Direction: String, CaseIterable {
case 前
case 右
case 後
case 左
}
🤔「配列いじればswitch自体使わなくても実装できそうだな。」
func nextDirection(direction: String) -> String {
let allcases = Direction.allCases
let direction = Direction(rawValue: direction)!
let index = allcases.firstIndex(of: direction)!
if index == allcases.count-1 {
return allcases[0].rawValue
}else {
return allcases[index+1].rawValue
}
}
😍「かっこいい」
🤔「けどもうここまできたら関数をいじるんじゃなくて列挙型の中身をいじった方が良くないか?」
enum Direction: String {
case 前
case 右
case 後
case 左
var next: String {
switch self {
case .前:
return "右"
case .右:
return "後"
case .後:
return "左"
case .左:
return "前"
}
}
}
🤔「こんな感じになるな」
func nextDirection(direction: String) -> String {
let direction = Direction(rawValue: direction)!
return direction.next
}
😁「良い感じ!列挙型って便利!」
終わり