8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

switch 文で Optional Binding を使いたかった話

Last updated at Posted at 2015-11-11

皆さんは Optional Binding 使ってますか?私はガンガン使ってます!何より nil 判定と nil じゃない場合の値導入(Unwrap)が同時にできちゃうからとても便利で安全でかつソースコードもエレガントですからね!

え?Optional Binding が知らない??うーんまあググればいっぱい出てくるけどとりあえず簡単な例を出しときましょうか:

let dict = [1: 2, 3: 4]
let key = 1

if let value = dict[key] {
	print(value)
}

上記の if let value = dict[key] の部分が Optional Binding にあたりますね。要するに dictkey が定義されているかを判定し、定義されていたらその値を value に代入して print(value) するというコードです。

ところが上記のソースは if 文ですけど、switch 文でも Optional Binding を使いたい、と思ってるわけですよ。だって条件が増えるといちいち else if で書くのだるいじゃん?

というわけで早速やってみた:

// このコードは間違いです。
let dict = [1: 2, 3: 4]
let key = 1

switch key {
case let value = dict[key]:
	print(value)
	
default:
	break
}

とまあとりあえず書いてみたけど、Playground に怒られました。「Expect ':' after 'case'」だって。は?

というわけでいろいろ探してみたけど、どうやらそもそも switch 文で Optional Binding ができないようです。

そこで友人がこんな書き方を教えてくれました

let dict = [1: 2, 3: 4]
let key = 1

switch dict[key] {
case .Some(let value):
	print(value)
	
default:
	break
}

まあ確かにこれなら一応判定はできるんですけどね。ただ Optional Binding ではなく、Optional<T> のパターンマッチに過ぎないのです。まあ dict[key] の中身だけ見たいのでしたらこれで OK ですが、自分の場合それだけでなく key 自身による条件判断も switch 文の中でやる予定ですのでこれではダメだ。

というわけでいろいろ試行錯誤したけど Optional Binding がどうしても使えないようなので、仕方ない、ここで Tuple を使うことに妥協することにしました。それでソースコードはこんな風になります:

let dict = [1: 2, 3: 4]
let key = 1

switch (key, dict[key]) {
case (let key, _) where key < 0:
	print("Invalid")
	
case (_, let value?):
	print(value)
	
default:
	print("nil")
}

まあこれは参照したい辞書型が dict だけなのでまだいいんですけど、これ以上増えてくると case 文が地獄になりそうですね…(そもそも自分の場合は沢山の辞書を参照したくてこれを作ったわけでしてw)

ちなみに上記のコードで気づいた人もいるかもしれませんが、実はパターンマッチで Unwrap するとき、case .Some(let value): のシンタックスシュガーとして case let value?: が使えることを色々調べてるうちにわかったのです。まあこっちの方は少しくらいは書きやすいですね。と一応収穫もありました。(と自分に言い聞かせる。)


追記

さらに無理やりな方法を思いついたのでここで追記しますw

let dict1 = [1: 2, 3: 4]
let dict2 = [5: 6, 7: 8]
let key = 5

var value = 0

switch key {
case let key where {() -> Int? in if let res = dict1[key] { value = res }; return dict1[key]}() != nil:
	print(value)
	
case let key where {() -> Int? in if let res = dict2[key] { value = res }; return dict2[key]}() != nil:
	print(value)
	
default:
	print("nil")
	
}

あらかじめ結果用の変数を用意して Closure を where に無理やりぶち込んで判定させるやり方です。もう何が何だか…
ちなみにここのサンプルコードは両方の case の中身が同じ print(value) ですがもちろん実際のコードでは中身は違うのです。


さらに追記

上記のコードをもう少し使いやすくしてみた。これならまあ使ってもいいかな?

let dict1 = [1: 2, 3: 4]
let dict2 = [5: 6, 7: 8]
let key = 5

var value = 0
let getDictValueFor = {(key: Int, fromDict dict: [Int: Int], inout andWriteTo value: Int) -> Int? in
	if let result = dict[key] {
		value = result
	}
	return dict[key]
}

switch key {
case let key where getDictValueFor(key, fromDict: dict1, andWriteTo: &value) != nil:
	print(value)
	
case let key where getDictValueFor(key, fromDict: dict2, andWriteTo: &value) != nil:
	print(value)
	
default:
	print("nil")
	
}

まああんまり inout 使いたくない感はなくはないけどまあ仕方ない。

8
7
2

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
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?