LoginSignup
11
10

More than 5 years have passed since last update.

範囲変数を使った case の書き方

Last updated at Posted at 2015-02-12

Swift の switch-case 文は使える書き方が非常に多くて強力で、とくに範囲が使えるのは非常に助かりますね(以前言ってた効率面の問題はまあ別として)。例えば極端なはなしではありますが、このような書き方もできます:

let x = Int(arc4random_uniform(1000))
switch x {
case 0 ..< 100:
    println("x is smaller than 100")

default:
    println("x is larger than 100")
}

これを普通の書き方でcase 0, 1, 2, 3………と延々と書くとさすがにだるすぎて心が折れそうですね。
ところがこんな書き方ができるのはいいんですけど、おそらく内部処理としてはコンパイル時にこの範囲を展開しているだけで、case 文自体は範囲変数に対応していないと思います、多分。なので以下のようにここの 0 ..< 100 を範囲変数として入れておくと文法エラーが起こります

let x = Int(arc4random_uniform(1000))
let range = 0 ..< 100
switch x {
case range:
    println("x is smaller than 100")

default:
    println("x is larger than 100")
}

今まで筆者は「何なんだよ範囲変数使えないのかよ(泣)」と涙目になりながら範囲を書いてその後ろにコメントで範囲変数を入れてたのですが、さっき気づいたことですけど case 文で where が使えるし更に find は範囲変数に対しても有効なので、こんな書き方も出来ます:

let x = Int(arc4random_uniform(1000))
let range = 0 ..< 100
switch x {
case let x where find(range, x) != nil:
    println("x is smaller than 100")

default:
    println("x is larger than 100")
}

まあ書き方がちょっと面倒なのはしかたないですが、これで範囲変数をそのまま利用できるのは非常に大きいと思うんですね。ただ気持ち悪いのはなんで case の直後に let X を書かないといけないのか…なんでそのまま case find… はダメなのか…というところです

ちなみにどうでもいい事言うとここの let は switch になっているものを更に別の定数に代入するというプロセスで、アップルの公式テキストブックでは

let vegetable = "red pepper"
switch vegetable {
    //何かのケース

case let x where x.hasSuffix("pepper"):
    //何かしらの pepper の場合

default:
    //その他
}

という例文がありますので、これからもお察しの通り、別に case let の後ろはなんだっていいんです。本当になんでここで一回 let かます必要があるんですかね…(だってここでは書いてないけど where のうしろに実は vegetable.hasSuffix("pepper") もちゃんと動くんだよ…


ところが karupanerura の記事でふっと気づきました…もしかして単に変数の型が違うだけじゃね?というわけで、早速検証してみましょう。まずは通常の範囲変数宣言:

let testRange = 0 ..< 100

これで opt キー押しながら testRange 変数をクリックしてみましょう。すると let testRange: Range<Int> の説明が出てくるはずです。ところが上記の karupanerura さんの記事によりますと ..< は HalfOpenInterval<T> 型だそうです。なるほど。確かに微妙な違いではありますが型が違うと強い静的型付け言語である Swift は受け付けてくれないわけですね。なんでデフォルトでは Range 型で宣言するかの問題は置いとくとして。というわけで型を明示して宣言してみましょう:

let x = Int(arc4random_uniform(1000))
let range: HalfOpenInterval<Int> = 0 ..< 100

switch x {
case range:
    println("x is smaller than 100")

default:
    println("x is larger than 100")
}

はい、これで普通に case range: で書いてもコンパイル通ります!やったね!

11
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10