0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Swift]caseのちょっと奥の方

Last updated at Posted at 2025-02-10

なにこれ?

前回[Swift] caseの使い方を覚えて一段上のプログラミングをの続きにあたります。
caseをもうちょっと深く掘り下げてみます。

以下ではThe Swift Programming LanguageのSummary of the Grammarを引用していきます。

この部分が引用です。

BNF記法っぽいやつ

Summary of the Grammarを見ていきます。
まずforは以下のようになっています。

for-in-statement → for case? pattern in expression where-clause? code-block

for-caseの後ろに続くのはpatternであることが分かりました。

次にwhileifおよびguardを見てみましょう。

while-statement → while condition-list code-block

if-statement → if condition-list code-block else-clause?

guard-statement → guard condition-list else code-block

これらは全てcaseの後ろはcondition-listとなっています。
condition-listは以下の通りです。

condition-list → condition | condition , condition-list
condition → expression | availability-condition | case-condition | optional-binding-condition
case-condition → case pattern initializer

こちらもcaseに続くものはpatternです。
最後にswitchです。

switch-statement → switch expression { switch-cases? }
switch-cases → switch-case switch-cases?
switch-case → case-label statements
switch-case → default-label statements
switch-case → conditional-switch-case
case-label → attributes? case case-item-list :
case-item-list → pattern where-clause? | pattern where-clause? , case-item-list

ちょっと込み入っていますが、他と同様caseの後ろに続くものはpatternです。

pattern

caseの後ろに続くものは常にpatternである事が分かりました。
ではpatternにはどのようなものはあるのかを見ていきましょう。

pattern → wildcard-pattern type-annotation?
pattern → identifier-pattern type-annotation?
pattern → value-binding-pattern
pattern → tuple-pattern type-annotation?
pattern → enum-case-pattern
pattern → optional-pattern
pattern → type-casting-pattern
pattern → expression-pattern

結構ありますね。
順番に詳しく見ていきます。

wildcard-pattern

pattern → wildcard-pattern type-annotation?
wildcard-pattern → _

前回ちょこっと出てきた何にでもマッチする _ です。

let value = 10

switch value {
  case 0: print("0")
  case 1: print("1")
  case _: print("other")
}

type-annotationが使えますが、あまり使いどころはないと思われます。

identifier-pattern

pattern → identifier-pattern type-annotation?
identifier-pattern → identifier

identifierつまり変数名や関数名タイプ名などがこれに当たります。

let range = 0...10
let i = 3

if case range = i {
  print("\(i) is in 0 to 10")
}

などです。

value-binding-pattern

pattern → value-binding-pattern
value-binding-pattern → var pattern | let pattern

これはパターンにマッチした値を取得するものです。

let value = 10

switch value {
  case let v: print("value is \(v)")
}

またlet/varの後にはpatternが続きますが、どこかに値を格納するためのidentifier-patternを含む必要があります。

tuple-pattern

pattern → tuple-pattern type-annotation?
tuple-pattern → ( tuple-pattern-element-list? )
tuple-pattern-element-list → tuple-pattern-element | tuple-pattern-element , tuple-pattern-element-list
tuple-pattern-element → pattern | identifier : pattern

これはそのまま、タプルを検査するものです。

let tuple = (0, "a")

switch tuple {
  case (0, "a"): print("left is 0 and right is a")
  case (0, _): print("left is zero")
  case (_, "a"): print("right is a")
  default: print("other")
}

() の中身がpatternであることに注意してください。

optional-pattern

pattern → optional-pattern
optional-pattern → identifier-pattern ?

let optionalValue: Int? = 0
if case let wrappedValue? = a {
  print("\(wrappedValue)")
}

value-binding-patternletの後に続くpatternoptional-patternになっています。

type-casting-pattern

pattern → type-casting-pattern
type-casting-pattern → is-pattern | as-pattern
is-pattern → is type
as-pattern → pattern as type

特定の型の場合にマッチするパターンです。

let value: Any = 0

switch value {
  case is Int: print("Int")
  case let s as String: print("String \(s)")
  default: print("other")
}

expression-pattern

pattern → expression-pattern
expression-pattern → expression
いっぱいありすぎなので省略

パターンマッチ演算子( ~= )が利用可能なすべてのがこれに当てはまります。

struct S {
  var range: ClosedRange<Int> = 0...1
  
  static func ~= (l: Self, r: Int) -> Bool { l.range ~= r }
  
  func newRange() -> ClosedRange<Int> { 0..1 }
}

let value = 10
switch value {
  case ..<0: ()
  case 0: ()
  case S(): ()
  case S().range: ()
  case S().newRange(): ()
  default: ()
}

ここからが本題です

enum-case-pattern

このパターンはあえて後ろに回していました。

pattern → enum-case-pattern
enum-case-pattern → type-identifier? . enum-case-name tuple-pattern?

type-identifierはenumの型名です。省略可能です。
enum-case-nameはenumの要素名です。
そして、そのあとに続くのがtuple-patternです。
caseにおいてenumの要素名の後ろに続く()はAssociated Valueの()ではなくtuple-pattern()です。

tuple-patternのGrammarを改めてみてみましょう。

pattern → tuple-pattern type-annotation?
tuple-pattern → ( tuple-pattern-element-list? )
tuple-pattern-element-list → tuple-pattern-element | tuple-pattern-element , tuple-pattern-element-list
tuple-pattern-element → pattern | identifier : pattern

taple-patternのタプルの要素部分に入るものはpatternです。
patternであればなんでも入れてよいのです。
enum-case-patternを含めて今まで出てきたすべてのパターンが対象です。

まず普通のパターンです。

enum E { case a, b, c }
let e = E.a

switch e {
  case .a: ()
  case .b: ()
  case .c: ()
}

つづいてAssociated Valueを持つenumのパターンです。

enum E {
  case value1(Int)
  case value2(Int, String)
}
let e = E.value1(10)

switch e {
  case .value1(0): print("value1 0")
  case .value1(..<0): print("value1 minus")
  case .value1(let i): print("value1 \(i)")

  case .value2(0, _): print("value2 0")
  case .value2(_, let string): print("value2 \(string)")
  case .value2(0...10, "str"): print("value2 0 to 10 and str");

  default: ()
}

enumの要素名の後ろの()taple-pattern()ですので結構なんでも入ります。

ではさらに

enum AnyBox {
  case value(Any)
}
let e = AnyBox.value(0)

switch e {
  case .value(is Int): print("value is Int")
  case .value(let str as String): print("value is String(\(str))")

  default: ()
}

type-casting-patternももちろん大丈夫です。

さらには

indirect Box {
  case .value(Int)
  case .box(Box)
}
let e = Box.box(.value(8))

switch e {
  case .box(.value(0...10)): print("0 to 10 in box")

  default: ()
}

enum-case-patternの入れ子も大丈夫です。

enum-case-patternは大抵の無茶は通ってしまいます。

まとめ

やはり、Swiftのcaseenumの組み合わせは極めて強力といえるでしょう。

caseを上手に使いこなして一歩進んだプログラムを書いていきましょう。

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?