参考資料

基本演算子

演算子は、値の確認、変更、または結合に使用する特別な記号やフレーズです。例えば、加算演算子(+)は let i = 1 + 2 のように2つの数値を加算し、論理AND演算子(&&)は if enteredDoorCode && passedRetinaScan のように2つのブール値を結合します。

基本演算子

演算子は、値の確認、変更、または結合に使用する特別な記号やフレーズです。例えば、加算演算子(+)は let i = 1 + 2 のように2つの数値を加算し、論理AND演算子(&&)は if enteredDoorCode && passedRetinaScan のように2つのブール値を結合します。

SwiftはCなどの言語で馴染みのある演算子をサポートしており、一般的なコーディングエラーを排除するためにいくつかの機能を改善しています。代入演算子(=)は値を返さないため、等価演算子(==)を使用すべきところで誤って使用されることを防ぎます。算術演算子(+-*/% など)は値のオーバーフローを検出して許可しません。これにより、型が格納できる値の範囲よりも大きくなったり小さくなったりした場合の予期しない結果を回避できます。Swiftのオーバーフロー演算子を使用して、値のオーバーフロー動作を有効にすることもできます。詳細はオーバーフロー演算子を参照してください。

Swiftには、a..<ba...b のような、Cには存在しない範囲演算子も用意されており、値の範囲を表現するためのショートカットとして使用できます。

この章では、Swiftの一般的な演算子について説明します。高度な演算子では、Swiftの高度な演算子について説明し、独自のカスタム演算子を定義する方法や、独自のカスタム型に標準演算子を実装する方法を解説します。

用語

演算子には単項、二項、三項があります:

  • 単項演算子は、単一のターゲットに対して操作します(例:-a)。単項前置演算子はターゲットの直前に配置され(例:!b)、単項後置演算子はターゲットの直後に配置されます(例:c!)。
  • 二項演算子は2つのターゲットに対して操作し(例:2 + 3)、2つのターゲットの間に配置されるため中置演算子と呼ばれます。
  • 三項演算子は3つのターゲットに対して操作します。Cと同様に、Swiftには三項条件演算子(a ? b : c)という1つの三項演算子のみがあります。

演算子が作用する値はオペランドと呼ばれます。式 1 + 2 では、+ 記号は中置演算子であり、その2つのオペランドは値 12 です。

代入演算子

代入演算子a = b)は、a の値を b の値で初期化または更新します:

let b = 10
var a = 5
a = b
// a is now equal to 10

代入の右側が複数の値を持つタプルの場合、その要素を複数の定数または変数に一度に分解できます:

let (x, y) = (1, 2)
// x is equal to 1, and y is equal to 2

CやObjective-Cの代入演算子とは異なり、Swiftの代入演算子は値を返しません。次の文は無効です:

if x = y {
    // This isn't valid, because x = y doesn't return a value.
}

この機能により、等価演算子(==)を使用すべきところで代入演算子(=)が誤って使用されることを防ぎます。if x = y を無効にすることで、Swiftはこの種のエラーを回避できるようにしています。

算術演算子

Swiftは、すべての数値型に対して4つの標準算術演算子をサポートしています:

  • 加算(+
  • 減算(-
  • 乗算(*
  • 除算(/
1 + 2       // equals 3
5 - 3       // equals 2
2 * 3       // equals 6
10.0 / 2.5  // equals 4.0

CやObjective-Cの算術演算子とは異なり、Swiftの算術演算子はデフォルトで値のオーバーフローを許可しません。Swiftのオーバーフロー演算子(例:a &+ b)を使用して、値のオーバーフロー動作を有効にすることができます。オーバーフロー演算子を参照してください。

加算演算子はStringの連結にも使用できます:

"hello, " + "world"  // equals "hello, world"

剰余演算子

剰余演算子a % b)は、a の中に b が何回収まるかを計算し、余りの値(剰余として知られる)を返します。

注意 剰余演算子(%)は、他の言語ではモジュロ演算子とも呼ばれます。ただし、Swiftでの負の数に対する動作は、厳密に言えばモジュロ演算ではなく剰余演算です。

剰余演算子の動作を説明します。9 % 4 を計算するには、まず 9 の中に 4 がいくつ収まるかを計算します:

remainderInteger

9 の中に 4 は2つ収まり、剰余は 1 です(オレンジ色で表示)。

Swiftでは、これは次のように書きます:

9 % 4    // equals 1

a % b の答えを求めるために、% 演算子は次の式を計算し、remainder を出力として返します:

a = (b x some multiplier) + remainder

ここで、some multipliera の中に収まる b の最大の倍数です。

94 をこの式に代入すると:

9 = (4 x 2) + 1

a が負の値の場合も、同じ方法で剰余を計算します:

-9 % 4   // equals -1

-94 を式に代入すると:

-9 = (4 x -2) + -1

剰余の値は -1 になります。

b が負の値の場合、b の符号は無視されます。つまり、a % ba % -b は常に同じ答えになります。

単項マイナス演算子

数値の符号は、前置の - を使用して切り替えることができます。これは単項マイナス演算子として知られています:

let three = 3
let minusThree = -three       // minusThree equals -3
let plusThree = -minusThree   // plusThree equals 3, or "minus minus three"

単項マイナス演算子(-)は、操作する値の直前に空白なしで配置されます。

単項プラス演算子

単項プラス演算子+)は、操作する値を変更せずにそのまま返します:

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

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

複合代入演算子

Cと同様に、Swiftは代入(=)と別の操作を組み合わせた複合代入演算子を提供しています。一例として加算代入演算子+=)があります:

var a = 1
a += 2
// a is now equal to 3

a += 2a = a + 2 の省略形です。事実上、加算と代入が1つの演算子に組み合わされ、両方のタスクを同時に実行します。

注意 複合代入演算子は値を返しません。例えば、let b = a += 2 とは書けません。

Swift標準ライブラリが提供する演算子の詳細については、演算子宣言を参照してください。

比較演算子

Swiftは次の比較演算子をサポートしています:

  • 等しい(a == b
  • 等しくない(a != b
  • より大きい(a > b
  • より小さい(a < b
  • 以上(a >= b
  • 以下(a <= b

注意 Swiftは、2つのオブジェクト参照が同じオブジェクトインスタンスを参照しているかどうかをテストするための2つの同一性演算子===!==)も提供しています。詳細については、同一性演算子を参照してください。

各比較演算子は、文が真かどうかを示す Bool 値を返します:

1 == 1   // true because 1 is equal to 1
2 != 1   // true because 2 isn't equal to 1
2 > 1    // true because 2 is greater than 1
1 < 2    // true because 1 is less than 2
1 >= 1   // true because 1 is greater than or equal to 1
2 <= 1   // false because 2 isn't less than or equal to 1

比較演算子は、if 文などの条件文でよく使用されます:

let name = "world"
if name == "world" {
    print("hello, world")
} else {
    print("I'm sorry \(name), but I don't recognize you")
}
// Prints "hello, world", because name is indeed equal to "world".

if 文の詳細については、制御フローを参照してください。

同じ型で同じ数の値を持つ2つのタプルを比較できます。タプルは左から右に、一度に1つの値ずつ比較され、等しくない2つの値が見つかるまで続きます。その2つの値が比較され、その比較結果がタプル比較全体の結果を決定します。すべての要素が等しい場合、タプル自体も等しくなります。例:

(1, "zebra") < (2, "apple")   // true because 1 is less than 2; "zebra" and "apple" aren't compared
(3, "apple") < (3, "bird")    // true because 3 is equal to 3, and "apple" is less than "bird"
(4, "dog") == (4, "dog")      // true because 4 is equal to 4, and "dog" is equal to "dog"

上記の例では、最初の行で左から右への比較動作を確認できます。12 より小さいため、(1, "zebra")(2, "apple") より小さいと見なされ、タプル内の他の値に関係なく比較が決定されます。"zebra""apple" より小さくないかどうかは問題ではありません。比較はすでにタプルの最初の要素で決定されているためです。ただし、タプルの最初の要素が同じ場合、2番目の要素が比較されます。これが2行目と3行目で起こっていることです。

タプルは、それぞれのタプルの各値に演算子を適用できる場合にのみ、特定の演算子で比較できます。例えば、以下のコードで示されているように、StringInt の両方の値が < 演算子を使用して比較できるため、型 (String, Int) の2つのタプルを比較できます。対照的に、Bool 値には < 演算子を適用できないため、型 (String, Bool) の2つのタプルは < 演算子で比較できません。

("blue", -1) < ("purple", 1)        // OK: Evaluates to true.
("blue", false) < ("purple", true)  // Error: Can't use < to compare Boolean values.

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

三項条件演算子

三項条件演算子は、3つの部分を持つ特別な演算子で、question ? answer1 : answer2 の形式をとります。question が真か偽かに基づいて、2つの式のどちらかを評価するためのショートカットです。question が真の場合、answer1 を評価してその値を返します。そうでない場合は、answer2 を評価してその値を返します。

三項条件演算子は、以下のコードの省略形です:

if question {
    answer1
} else {
    answer2
}

これは、テーブル行の高さを計算する例です。行にヘッダーがある場合、行の高さはコンテンツの高さより50ポイント高く、ヘッダーがない場合は20ポイント高くなります:

let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight is equal to 90

上記の例は、以下のコードの省略形です:

let contentHeight = 40
let hasHeader = true
let rowHeight: Int
if hasHeader {
    rowHeight = contentHeight + 50
} else {
    rowHeight = contentHeight + 20
}
// rowHeight is equal to 90

最初の例での三項条件演算子の使用により、rowHeight を1行のコードで正しい値に設定できます。これは2番目の例で使用されているコードよりも簡潔です。

三項条件演算子は、2つの式のどちらを考慮するかを決定するための効率的なショートカットを提供します。ただし、三項条件演算子は注意して使用してください。簡潔さは、使いすぎると読みにくいコードにつながる可能性があります。三項条件演算子の複数のインスタンスを1つの複合文に組み合わせることは避けてください。

Nil合体演算子

Nil合体演算子a ?? b)は、オプショナル a に値が含まれている場合はアンラップし、anil の場合はデフォルト値 b を返します。式 a は常にオプショナル型です。式 ba 内に格納されている型と一致する必要があります。

Nil合体演算子は、以下のコードの省略形です:

a != nil ? a! : b

上記のコードは、三項条件演算子と強制アンラップ(a!)を使用して、anil でない場合は a 内にラップされた値にアクセスし、そうでない場合は b を返します。Nil合体演算子は、この条件チェックとアンラップを簡潔で読みやすい形式でカプセル化するためのより洗練された方法を提供します。

注意 a の値が非 nil の場合、b の値は評価されません。これは短絡評価として知られています。

以下の例では、Nil合体演算子を使用して、デフォルトの色名とオプショナルのユーザー定義色名のどちらを選択するかを決定しています:

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

var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName is nil, so colorNameToUse is set to the default of "red"

userDefinedColorName 変数は、デフォルト値 nil を持つオプショナル String として定義されています。userDefinedColorName はオプショナル型であるため、Nil合体演算子を使用してその値を考慮できます。上記の例では、この演算子を使用して colorNameToUse という String 変数の初期値を決定しています。userDefinedColorNamenil であるため、式 userDefinedColorName ?? defaultColorNamedefaultColorName の値、つまり "red" を返します。

userDefinedColorName に非 nil 値を割り当てて、Nil合体演算子のチェックを再度実行すると、デフォルトの代わりに userDefinedColorName 内にラップされた値が使用されます:

userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName isn't nil, so colorNameToUse is set to "green"

範囲演算子

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

閉範囲演算子

閉範囲演算子a...b)は、a から b までの範囲を定義し、値 ab を含みます。a の値は b より大きくてはなりません。

閉範囲演算子は、for-in ループのように、すべての値を使用したい範囲を反復処理する場合に便利です:

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

for-in ループの詳細については、制御フローを参照してください。

半開範囲演算子

半開範囲演算子a..<b)は、a から b までの範囲を定義しますが、b を含みません。最初の値は含むが最後の値は含まないため、半開と呼ばれます。閉範囲演算子と同様に、a の値は b より大きくてはなりません。a の値が b と等しい場合、結果の範囲は空になります。

半開範囲は、配列のようなゼロベースのリストを扱う場合に特に便利です。リストの長さまで(ただし含まない)カウントアップする場合に役立ちます:

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

配列には4つの項目が含まれていますが、0..<count は半開範囲であるため、3(配列の最後の項目のインデックス)までしかカウントしないことに注意してください。配列の詳細については、配列を参照してください。

片側範囲

閉範囲演算子には、一方向にできるだけ続く範囲のための代替形式があります。例えば、インデックス2から配列の終わりまでのすべての要素を含む範囲です。このような場合、範囲演算子の片側から値を省略できます。演算子の片側にのみ値があるため、この種の範囲は片側範囲と呼ばれます。例:

for name in names[2...] {
    print(name)
}
// Brian
// Jack

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

半開範囲演算子にも、最終値のみで記述される片側形式があります。両側に値を含む場合と同様に、最終値は範囲の一部ではありません。例:

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

片側範囲は、添え字だけでなく他のコンテキストでも使用できます。最初の値を省略した片側範囲では反復処理できません。反復がどこから始まるべきかが明確でないためです。最終値を省略した片側範囲では反復処理できますが、範囲が無限に続くため、ループに明示的な終了条件を必ず追加してください。以下のコードに示すように、片側範囲に特定の値が含まれているかどうかを確認することもできます。

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

論理演算子

論理演算子は、ブール論理値 truefalse を変更または結合します。SwiftはC系言語で見られる3つの標準論理演算子をサポートしています:

  • 論理NOT(!a
  • 論理AND(a && b
  • 論理OR(a || b

論理NOT演算子

論理NOT演算子!a)は、ブール値を反転させ、truefalse になり、falsetrue になります。

論理NOT演算子は前置演算子であり、操作する値の直前に空白なしで配置されます。次の例に見られるように、「a でない」と読むことができます:

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

フレーズ if !allowedEntry は「入場が許可されていない場合」と読むことができます。後続の行は、「入場が許可されていない」が真の場合にのみ実行されます。つまり、allowedEntryfalse の場合です。

この例のように、ブール定数と変数名を慎重に選択することで、二重否定や混乱した論理文を避けながら、コードを読みやすく簡潔に保つことができます。

論理AND演算子

論理AND演算子a && b)は、両方の値が true である場合にのみ式全体も true になる論理式を作成します。

いずれかの値が false の場合、式全体も false になります。実際、最初の値が false の場合、2番目の値は評価されません。式全体を true にすることは不可能だからです。これは短絡評価として知られています。

この例では、2つの Bool 値を考慮し、両方の値が true の場合にのみアクセスを許可します:

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

論理OR演算子

論理OR演算子a || b)は、2つの隣接するパイプ文字から構成される中置演算子です。2つの値のうち1つだけが true であれば式全体が true になる論理式を作成するために使用します。

上記の論理AND演算子と同様に、論理OR演算子は短絡評価を使用して式を評価します。論理OR式の左側が true の場合、右側は評価されません。式全体の結果を変えることはできないためです。

以下の例では、最初の Bool 値(hasDoorKey)は false ですが、2番目の値(knowsOverridePassword)は true です。1つの値が true であるため、式全体も true と評価され、アクセスが許可されます:

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

論理演算子の組み合わせ

複数の論理演算子を組み合わせて、より長い複合式を作成できます:

if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "Welcome!"

この例では、複数の &&|| 演算子を使用して、より長い複合式を作成しています。ただし、&&|| 演算子は依然として2つの値に対してのみ操作するため、これは実際には3つの小さな式が連結されたものです。この例は次のように読むことができます:

正しいドアコードを入力して網膜スキャンに合格した場合、または有効なドアキーを持っている場合、または緊急オーバーライドパスワードを知っている場合は、アクセスを許可します。

enteredDoorCodepassedRetinaScan、および hasDoorKey の値に基づいて、最初の2つのサブ式は false です。ただし、緊急オーバーライドパスワードは知られているため、複合式全体は依然として true と評価されます。

注意 Swiftの論理演算子 &&|| は左結合です。つまり、複数の論理演算子を持つ複合式は、最も左のサブ式を最初に評価します。

明示的な括弧

厳密には必要でない場合でも、複雑な式の意図を読みやすくするために括弧を含めると便利なことがあります。上記のドアアクセスの例では、複合式の最初の部分に括弧を追加して、その意図を明示的にすると便利です:

if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "Welcome!"

括弧により、最初の2つの値が全体のロジックにおける別個の可能な状態の一部として考慮されることが明確になります。複合式の出力は変わりませんが、全体的な意図は読者にとってより明確になります。可読性は常に簡潔さよりも優先されます。意図を明確にするのに役立つ場合は括弧を使用してください。