Swift基本演算子（5.9.2）

# 基本演算子

演算子とは、値のチェック、変更、結合に使用する特別な記号または語句。

Swiftは一般的なコーディングエラーを排除するためにCのような言語の演算子からいくつかの機能を向上。

- 代入演算子 (=) は、等号演算子 (==) が意図されているときに誤って使用されるのを防ぐために、値を返しません
- 算術演算子（+、-、*、/、%など）は、値のオーバーフローを検出して許可しないようにし、数値を格納する型の許容値範囲よりも大きくなったり小さくなったりする数値を扱うときに予期しない結果にならないようにします

Swift のオーバーフロー演算子を使用することで、値のオーバーフロー動作を選択可能。

値の範囲を表現するためのショートカットとして範囲演算子を提供。

# 用語解説

演算子は単項、二項、または三項。

- 単項演算子は、1つの対象（-aなど）を操作
    - 単項前置演算子は対象の直前に表示（!bなど）
    - 単項後置演算子は対象の直後に表示（c!）
- 二項演算子は2つのターゲット（2 + 3など）に対して演算を行い、2つのターゲットの間に表示されるため中置語
- 三項演算子は3つのターゲット上で動作
    - 三項演算子
    - 三項条件演算子 (a ? b : c)

演算子が影響する値はオペランド。

- 式 1 + 2 では、+ 記号は中置演算子であり、その 2 つのオペランドは値 1 と 2

# 代入演算子

代入演算子（a = b）は、aの値をbの値で初期化または更新。

```swift
let b = 10
var a = 5
a = b
```

代入の右辺が複数の値を持つタプルの場合、その要素は一度に複数の定数または変数に分解可能。

```swift
let (x, y) = (1, 2)
```

if x = yは無効なコード。

- 代入演算子は、それ自体が値を返さない
- 等号演算子 (==) が実際に意図されているときに、代入演算子 (=) が誤って使用されるのを防ぐ

# 算術演算子

すべての数値型のための四則演算子をサポート。

- 加算 (+)
- 減算 (-)
- 乗算 (*)
- 除算 (/)

```swift
1 + 2       // equals 3
5 - 3       // equals 2
2 * 3       // equals 6
10.0 / 2.5  // equals 4.0
```

算術演算子は、デフォルトでオーバーフローする値を許可しない。

- オーバーフロー演算子（a &+ b など）を使用することで、値のオーバーフロー動作を選択可能

加算演算子は文字列の連結にも対応。

```swift
"hello, " + "world"  // equals "hello, world"
```

# 剰余演算子

余り演算子（a % b）は、bの何乗がaの中に収まるかを計算し、残った値（余りとして知られている）を返す。

```swift
9 % 4    // equals 1
-9 % 4   // equals -1
```

## 単項マイナス演算子

数値の符号は、単項マイナス演算子として知られる接頭辞 - を使って切り替え可能。

単項マイナス演算子（-）は、空白を入れずに値の直前に付加。

```swift
let three = 3
let minusThree = -three       // equals -3
let plusThree = -minusThree   // equals 3
```

## 単項プラス演算子

単項プラス演算子（+）は、操作した値をそのまま返す。

単項演算子プラスは実際には何もしないが、負の数に対して単項演算子マイナスを使うときに、正の数に対するコードの対称性を持たせるために使うことができる。

```swift
let minusSix = -6
let alsoMinusSix = +minusSix  // equals -6
```

## 複合代入演算子

代入 (=) と別の操作を組み合わせた複合代入演算子を提供。

- 加算代入演算子(+=)

```swift
var a = 1
a += 2
// 3
```

a += 2という式は、a = a + 2の省略形。

- 加算と代入は1つの演算子に統合され、両方のタスクを同時に実行

<aside>
💡 複合代入演算子は値を返しません。例えば、let b = a += 2とは書けない。

</aside>

# 比較演算子

Swift は以下の比較演算子をサポート。

- イコール (a == b)
- と等しくない (a != b)
- より大きい (a > b)
- より小さい (a < b)
- 以上 (a >= b)
- 以下（a <= b）

<aside>
💡 2 つのオブジェクト参照が両方とも同じオブジェクトインスタンスを参照しているかどうかをテストするために使用する、2 つの同一性演算子 (=== と !==) を提供。

</aside>

各比較演算子は、ステートメントが真かどうかを示すBool値を返す。

```swift
1 == 1   // true
2 != 1   // true
2 > 1    // true
1 < 2    // true
1 >= 1   // true
2 <= 1   // false
```

比較演算子は、if文のような条件文でよく使用される。

```swift
let name = "world"
if name == "world" {
    print("hello, world")
    // hello, world
} else {
    print("I'm sorry \(name), but I don't recognize you")
}
```

**2つのタプルが同じ型で同じ数の値を持つ場合、それらを比較可能**

- タプルは左から右へ、一度に1つの値ずつ、等しくない2つの値が見つかるまで比較
- 比較結果がタプル比較の全体的な結果を決定
- すべての要素が等しい場合、タプル自体も等しい

```swift
(1, "zebra") < (2, "apple")   // true
(3, "apple") < (3, "bird")    // true
(4, "dog") == (4, "dog")      // true
```

タプルを所定の演算子で比較できるのは、その演算子がそれぞれのタプルの各値に適用できる場合

- (String, Int)型の2つのタプルは比較可能
- (String, Bool)型の2つのタプルは<演算子で比較不能

<aside>
💡 Swift の標準ライブラリには、7 つ未満の要素を持つタプルの比較演算子が含まれています。7つ以上の要素を持つタプルを比較するには、比較演算子を自分で実装する必要があります。

</aside>

# 三項条件演算子

三項条件演算子は3つの部分を持つ特殊な演算子。

- question ? answer1 : answer2
    - questionがtrueの場合はanswer1を評価してその値を返す
    - そうでない場合、answer2を評価してその値を返す
- questionが真か偽かに基づいて、2つの式のうちの1つを評価するためのショートカット

三項条件演算子は、以下のコードの略記法。

```swift
if question {
    answer1
} else {
    answer2
}
```

```swift
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// 90
```

上記の例は、以下のコードの省略形。

```swift
let contentHeight = 40
let hasHeader = true
let rowHeight: Int
if hasHeader {
    rowHeight = contentHeight + 50
    // 90
} else {
    rowHeight = contentHeight + 20
}
```

三項条件演算子は、2つの式のどちらを考慮すべきかを決めるための効率的な省略記法

- 三項条件演算子の使用には注意が必要
- **簡潔さは、使いすぎると読みにくいコードになりかねない**
- **三項条件演算子の複数のインスタンスを1つの複合文にまとめることは避ける**

# ****Nil結合演算子****

Nil結合演算子（a ?? b）は、オプショナル型 a に値が含まれている場合はそのラップを解除。

- a が nil の場合はデフォルト値 b を返す
- 式aは常にオプショナル型
- 式bはaの中に格納されている型と一致する必要がある

Nil結合演算子は、以下のコードの略記法。

```swift
a != nil ? a! : b
```

上のコードでは、三項条件演算子と強制アンラッピング（a！）を使用。

- aがnilでないときはaにラップされた値にアクセス
- そうでないときはbを返す
- Nil結合演算子は、この条件チェックとアンラップを簡潔で読みやすい形でカプセル化する、よりエレガントな方法を提供

<aside>
💡 aの値がnilでない場合、bの値は評価されない。これは短絡評価として知られている。

</aside>

```swift
let defaultColorName = "red"
var userDefinedColorName: String?
// defaults to nil

var colorNameToUse = userDefinedColorName ?? defaultColorName
// red

userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// green
```

# 範囲演算子

Swift には、値の範囲を表現するためのショートカットである、いくつかの範囲演算子が含まれています。

## 閉範囲演算子

閉範囲演算子（a...b）は、aからbまでの範囲を定義し、aおよびbの値を含む。

- for-inループなど、すべての値を使用したい範囲を反復処理するときに便利

```swift
for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
    // 1 times 5 is 5
    // 2 times 5 is 10
    // 3 times 5 is 15
    // 4 times 5 is 20
    // 5 times 5 is 25
}
```

## 半開放範囲演算子

半開放範囲演算子(a..<b)は、aからbまでの範囲を定義しますが、bを含みません。

- 閉範囲演算子と同様、aの値はbより大きくてはいけない
- aの値がbと等しい場合、結果の範囲は空
- 配列のようなゼロベースのリストを扱うときに特に便利
    - リストの長さ（を含まない）までカウントするのに便利

```swift
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
    print("Person \(i + 1) is called \(names[i])")
    // Person 1 is called Anna
    // Person 2 is called Alex
    // Person 3 is called Brian
    // Person 4 is called Jack
}
```

## 片側範囲演算子

閉範囲演算子には、可能な限り一方向に続く範囲のための別の形式が存在。

- インデックス2から配列の最後まで、配列のすべての要素を含む範囲
- このような場合、範囲演算子の片側から値を省略可能
- このような範囲を片側範囲と呼ぶ
    - 演算子の片側にしか値がないため

```swift
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names[2...] {
    print(name)
    // Brian
    // Jack
}

for name in names[...2] {
    print(name)
    // Anna
    // Alex
    // Brian
}
```

半開放範囲演算子にも、最終値だけを書く片開きの形が存在

- 両辺に値を含むときと同じように、最終値は範囲の一部ではない

```swift
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names[..<2] {
    print(name)
    // Anna
    // Alex
}
```

片側範囲は添え字だけでなく、他のコンテキストでも使用可能

- 最初の値を省略した片側範囲を反復処理することはできない
- 範囲は無限に続くので、ループの終了条件を明示的に追加する必要がある
- 片側範囲が特定の値を含むかどうかをチェックすることも可能

```swift
let range = ...5
range.contains(7)   // false
range.contains(4)   // true
range.contains(-1)  // true
```

# 論理演算子

論理演算子は、ブール論理値 true と false を変更または組み合わせます。

- 論理 NOT (!a)
- 論理 AND (a && b)
- 論理 OR (a || b)

## 論理NOT演算子

論理NOT演算子（！a）

- ブール値を反転させ、trueがfalseに、falseがtrueとなる
- 前置演算子で、演算する値の直前に空白を入れない

```swift
// if !allowedEntry は、"if not allowed entry "と読める。
// 後続の行は、"not allowed entry "がtrueの場合、つまりallowedEntryがfalseの場合にのみ実行される。

let allowedEntry = false
if !allowedEntry {
    print("ACCESS DENIED")
    // ACCESS DENIED
}
```

ブール定数や変数名を注意深く選択することで、二重否定や紛らわしい論理文を避けつつ、読みやすく簡潔なコードを保つことが可能。

## 論理AND演算子

論理AND演算子（a && b）

- 論理式全体が真であるためには両方の値が真でなければならないという論理式を作成
- どちらかの値が偽の場合、全体の式も偽となる
- 短絡評価
    - 1つ目の値が偽の場合、2つ目の値は評価されない

```swift
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
    // ACCESS DENIED
}
```

## 論理OR演算子

論理和演算子(a || b)

- 隣接する2つのパイプ文字から作られる中置演算子
- 2つの値のうち1つだけが真であれば式全体が真となる論理式を作成するために使用
- 短絡評価
    - 論理OR式の左辺が真である場合、右辺は評価されない

```swift
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
    print("Welcome!")
    // Welcome!
} else {
    print("ACCESS DENIED")
}
```

## 論理演算子の組み合わせ

複数の論理演算子を組み合わせて、より長い複合式を作成可能。

```swift
let enteredDoorCode = true
let passedRetinaScan = false
let hasDoorKey = false
let knowsOverridePassword = true
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
    // Welcome!
} else {
    print("ACCESS DENIED")
}
```

<aside>
💡 論理演算子 && と || は左結合で、複数の論理演算子を持つ複合式は左端の部分式を最初に評価することを意味。

</aside>

## 明示的な括弧

複雑な式の意図を読みやすくするために、厳密には必要でないときに括弧を入れると便利ことがある。

```swift
let enteredDoorCode = true
let passedRetinaScan = false
let hasDoorKey = false
let knowsOverridePassword = true
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
    // Welcome!
} else {
    print("ACCESS DENIED")
}
```

複合式の最初の部分に括弧を追加して、その意図を明示するのが便利。

括弧は、最初の2つの値が、全体的なロジックの中で別の可能性のある状態の一部と見なされていることを明確化。

複合式の出力は変わりませんが、全体的な意図は読み手に明確に伝わる。

- **読みやすさは常に簡潔さよりも優先される**

括弧は、意図を明確にするのに役立つ場合に使用。

# 参考

[Documentation](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/basicoperators/)