CHAPTER 04 オプショナル

今時の言語にはすべて搭載されている、ヌルポ対策機能。

オプショナル型と nil

Swift では null でなく nil で Objective-C には nilNull も両方あるらしい。 null 許容型 IntInt? とかく。これは Kotlin 等と同様。

オプショナル型の値を開示する

Int? 型を Int 型に変換するのに ! を使用する。確実に nil が入っていないと予想できるときに使用する:

let year: Int? = Int("2020")
var remain: Int = year! - 2016  // ! が無いとコンパイルエラー
year! -= 2016  // このように代入も可能

if-let 文

オプショナル型の値が nil ではなかった場合に処理をする、といった構文が if-let 文となる:

let year: Int? = Int("2020")
if let y = year {
    print("あと \(y - 2014) 年")  // y: Int として使える
} else {
    print("エラー")
}

// 複数のオプショナルがともに nil でない場合は以下のように書く
if let sapporo = Int("1972"), nagano = Int("1998") {
    print("\(nagano - sapporo) years.")
}

// where 句で更に条件を絞ることができる
if let sapporo = Int("1972"), nagano = Int("1998") where nagano > 1990 {
    print("\(nagano - sapporo) years.")
}

guard 文

if-let の逆、即ち条件を満たさない時に else 句の処理を行うことができる:

let stock = ["01", "2", "4", "05", "8", "q", "X"]
for str in stock {
    guard let v = Int(str) else {
        print(str + "??")
        break
    }
    print(v, terminator:" ")
}
// 1 2 4 5 8 q??

nil 合体演算子

オプショナル型に対し ?? で値を取り出すことで nil の場合にデフォルト値を使用するといったことができる:

opv ?? S  // opv が nil なら S が使われる. opv != nil ? opv! : S と同様

有値オプショナル型

プログラムの前後関係から確実に nil でない値が入っていると予想できる場合は Int? でなく Int! とすると使用する際に ! の開示が不要となる:

let year: Int! = Int("2020")
print("あと \(year - 2016) 年")  // ! が不要

失敗のあるイニシャライザの定義

init ではなく init? を使用する。例えば init への引数が防いで有効な構造体を生成できない時に nil を返すようにするなど。

CHAPTER 05 基本的なデータ型

部分配列

配列のスライスはどうやるのかと思ったら範囲指定がそのまま使用できるようだ:

let stock = ["01", "2", "4", "05", "8", "q", "X"]
print(stock[1...4])  // ["2", "4", "05", "8"]

但し上記でスライスした配列は Array<String> ではなく ArraySlice<String> になり、添字が元のままの 1, 2, 3, 4 になる。 これを普通の 0 から始まる配列として使用したい場合は Array<String> のイニシャライザで wrap する:

let subarray = [String](stock[1...4])
print(subarray[0])  // "2"

可変長引数

可変長引数指定は Java と一緒で Int... のようにする。 但し Java と異なり引数の途中でも可変長引数にできる。

辞書の型宣言と初期値

辞書の型は [String: Int] のように記述する。

var d: [String: Int] = [:]  // 空の辞書代入

辞書へのアクセス

「辞書のキーが存在したらなにか処理をする」のようなものは if-let 文が使える:

var d = ["Swift": 2014, "Objective-C": 1983]
if let v = d["Swift"] { print(v) }  // 2014
if let v = d["Ruby"] { print(v) }  // 何も出力されない

集合の型宣言と初期値

配列リテラルが使用できるが型を明示的に指定しないといけない:

var s: Set<String> = ["フランダース", "フランドル", "フランシスカ"]

CHAPTER 06 パターン

キーワード付きのタプル

タプルにキーワードが付けられるというのは知らなかった:

let photo = (file: "tiger.jpg", width: 640, height: 800)
print(photo.0)  // tiger.jpg
print(photo.file)  // tiger.jpg

タプルを switch 文で使う

結構柔軟なことができるようだ:

switch day {
case (1, 1...5):  // 範囲指定ができる
    print("正月休み")
case (4, 29), (5, 2...6):  // 更に条件を並べることができる
    print("連休")
case (8, let d) where d > 10:  // タプルの一部を使用することができるし where で条件を絞れる
    print("8/\(d)は夏休みです")
default:
    break
}

オプショナル型を switch 文で使う

オプショナル型を含むタプルに対し nil でないことを条件とするには ? を付けるようだ:

case let (name, age?) where age >= 18:  // 開示した数値を代入
case let (name, (15...18)?):  // 開示して区間指定

シンプルな列挙型

Java と似ているが case 接頭辞を付けるようだ:

enum Direction {
    case Up, Down, Right, Left
}

値型の列挙型

列挙型に実体型 (raw value) を割り当てることができるらしい:

enum Direction: Int {
    case Up = 0, Down, Right, Left  // こう書くと 0, 1, 2, 3 となる
}
print(Direction.Right.rawValue)  // 2
print(Direction(rawValue: 3)!)  // Left

共用型の列挙型

イマイチ使い所がよく分からないが、それぞれの要素の型 (タプル) が異なる列挙型も定義できる:

enum WebColor {
    case Name(String)
    case Code(String)
    case White, Black, Red
}

let background = WebColor.Name("indigo")
let tuiquoise = WebColor.Code("#04E0D0")
let textColor = WebColor.Black

if-case 文

switch 文だとパターンマッチングが使用できるが default 句を書かなければならず 1 つのパターンマッチングを行うだけだと冗長なので if-case 文という構文が用意されているようだ:

if case .card(let y, _) = t where y > 1200 { ...

for-in 文で case パターンを使う

for-in でも case が同様に使用できる。やはり switch だと冗長なので代わりに使用できるようだ。

再帰的な列挙型

……こんな構文使うのか?と思うので、メモだけ。自分自身の型を再帰的に使用する場合は case の前に indirect を指定し特別扱いする。