iBooks にある “The Swift Programming Language” の勉強メモ。Objective-C と C を普段書いている自分から、ちょっと馴染みがないものを特にまとめておきます。目次は こちら。
今回は、Enumerations に関して。
Enumerations
C において、Enumeration は、あるお互いに関係があるいくつかの名前を数字のセットに代入していました。Swift の Enumeration はより柔軟になったようです。数字以外にも、文字列や浮動小数点等も使用できます。
1. Enumeration Syntax
まずは、基本的な Swift での Enumeration。
1 2 3 4 5 6 | enum CompassPoint { case North case South case East case West } |
enum
キーワードで Enumeration を定義します。North や South は、この Enumeration のメンバー値 (member values)、またはメンバー(members)と呼ばれます。case
キーワードが新しいメンバーが定義されることを指示しています。
C や Objective-C では、Enumeration を定義すると、0,1,…n と自動的に値が推定されていましたが、Swift では違います。Swift では、Nort といったメンバーがそれ自体の値です。(fully-fledged values)
1 2 3 | enum Planet { case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune } |
このようにカンマで区切って書くこともできます。Enumeration 自体の名前は、上記の CompassPoint や Planet のように大文字で始めます。また、複数形ではなく、単数形を使用するべきです。
Enumeration は下記のように、変数に代入できます。
1 2 | var directionToHead = CompassPoint.West directionToHead = .East |
二度目の代入の際には、CompassPoint といった Enumeration 自体の名前は省略できます。
2. Enumeration と Switch 文
Enumeration と一緒によく使われるのが Switch 文ですね。
1 2 3 4 5 6 7 8 9 10 11 12 | directionToHead = .South switch directionToHead { case .North: println("Lots of planets have a north") case .South: println("Watch out for penguins") case .East: println("Where the sun rises") case .West: println("Where the skies are blue") } // prints "Watch out for penguins |
Switch 文に関する記事でも書いたように、Switch 文は exhaustive でないといけないので、このように全てのメンバーをタイプしなければなりません。そうでないと、コンパイルエラーになります。もちろん、全てのメンバーを列挙する代わりに、default
キーワードも使用できます。
3. 関連ある値 (Associated Values)
これまでの例では、値がそれぞれただ定義されていました。これに関連した値を各メンバーに持たせてあげることが役に立ちます。そして、毎度この関連した値を変更することで、柔軟になります。他の言語では、discriminated unions, tagged unions, variants と呼ばれるものらしいです。
例えば、こんな感じ。
1 2 3 4 | enum Barcode { case UPCA(Int, Int, Int) case QRCode(String) } |
これは、Barcode という Enumeration を定義しています。この Barcode は、UPCA か QRCode の 2 つのうちどちらかのメンバーになります。UPCA とは、普通の縦にたくさんある線があるバーコードのことです。それぞれのバーコードによって、関連する値は違います。
1 2 | var productBarcode = Barcode.UPCA(8, 85909_51226, 3) productBarcode = .QRCode("ABCDEFGHIJKLMNOP") |
まず、1行目で、Barcode.UPCA の変数を作成。この .UPCA は、関連した値として、3 つの整数を持っています。2 行目のように、違うタイプのバーコードを代入することもできます。
1 2 3 4 5 6 | switch productBarcode { case .UPCA(let numberSystem, let identifier, let check): println("UPC-A with value of \(numberSystem), \(identifier), \(check).") case .QRCode(let productCode): println("QR code with value of \(productCode).") } |
上記のようにcase
の中で、関連した値を取り出すことができます。上記のように全ての関連した値が、変数かコンスタントで統一されいれば、全ての var/let を書く必要はなく、メンバー名の前に書くことができます。
1 2 3 4 5 6 7 | switch productBarcode { case let .UPCA(numberSystem, identifier, check): println("UPC-A with value of \(numberSystem), \(identifier), \(check).") case let .QRCode(productCode): println("QR code with value of \(productCode).") } // prints "QR code with value of ABCDEFGHIJKLMNOP. |
4. Raw Values
関連ある値とは少し違った、raw values というものも使えます。これも各メンバーに対して、値を関連付けます。全ての値が同じ型である場合には、Enumeration 宣言時に、定義することができます。
1 2 3 4 5 | enum ASCIIControlCharacter: Character { case Tab = "\t" case LineFeed = "\n" case CarriageReturn = "\r" } |
Raw values は、関連ある値とは違い、Enumeration が宣言された時に、決定されます。そして、宣言時にデータ型を宣言しなければなりません。上記の例では、: Character
を省略できません。関連ある値は、新しい変数やコンスタントを Enumeration をもとに作成した時に決定されます。
また、Raw values は、各 Enumeration 内でユニークでなければなりません。整数が使用された場合には、C のように自動で値が代入されます。例えば、
1 2 3 | enum Planet: Int { case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune } |
何も指定しなくとも、Venus は 2 という raw values を持っています。1 ずつ増えるので、もちろんユニークです。
Raw values にアクセスするには、toRaw() メソッドを使用します。
1 | let earthsOrder = Planet.Earth.toRaw() |
逆に、fromRaw() メソッドを使用して、raw values からメンバー値を探すこともできます。
1 2 | let possiblePlanet = Planet.fromRaw(7) // possiblePlanet is of type Planet? and equals Planet.Uranus |