Swift 言語ガイド – 2. 基本的な演算子

Swift

はじめに

公式ページのSwift 言語ガイド「The Swift Programming Language Swift 5.3」に基づき記載いたします。本ページでは、「基本的な演算子」について記載いたします。
記載内容に誤り等ございましたら、ご連絡をいただければ幸いです。

2. 基本的な演算子

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

Swiftは、皆さんがCなどの言語で既に知っている演算子をサポートし、一般的なコーディングエラーを排除するためにいくつかの機能が改善されています。代入演算子(=)は値を返しません。これは、等しい演算子(==)が意図されているときに誤って使用されるのを防ぐためです。算術演算子(+,-,*,/,%など)は、値のオーバーフローを検出して禁止し、それらを格納する型の許容値の範囲よりも大きいまたは小さい数値を処理するときに予期しない結果を回避します。オーバーフロー演算子で説明されているように、Swiftのオーバーフロー演算子を使用して、値のオーバーフロー動作をオプトインできます。

Swiftは、値の範囲を表現するためのショートカットとして、a .. <bやa … bなどのCにはない範囲演算子も提供します。

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

2.1. 用語

演算子は、単項、2進数、または3進数です。

  • 単項演算子は、単一のターゲット(-aなど)を操作します。単項プレフィックス演算子はターゲットの直前に表示され(!bなど)、単項ポストフィックス演算子はターゲットの直後に表示されます(c!など)。
  • 二項演算子は2つのターゲット(2 + 3など)で動作し、2つのターゲットの間に表示されるため中置です。
  • 三項演算子は3つのターゲットで動作します。 Cと同様に、Swiftには3項演算子(a?b:c)が1つだけあります。

演算子が影響する値はオペランドです。 式1+2では、+記号は二項演算子であり、その2つのオペランドは値1と2です。

2.2. 代入演算子

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

let b = 10
var a = 5
a = b
// aは10に等しくなります

割り当ての右側が複数の値を持つタプルである場合、その要素は一度に複数の定数または変数に分解できます。

let (x, y) = (1, 2)
// xは1に等しく、yは2に等しい

CやObjective-Cの代入演算子とは異なり、Swiftの代入演算子自体は値を返しません。次のステートメントは無効です。

if x = y {
    // x = yは値を返さないため、これは無効です。
}

この機能は、等しい演算子(==)が実際に意図されているときに、代入演算子(=)が誤って使用されるのを防ぎます。x = yを無効にすることで、Swiftはコード内のこの種のエラーを回避するのに役立ちます。

2.3. 算術演算子

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

  • 加算(+)
  • 減算(-)
  • 掛け算(*)
  • 除算(/)
1 + 2       // 3に等しい
5 - 3       // 2に等しい
2 * 3       // 6に等しい
10.0 / 2.5  // 4.0に等しい

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

加算演算子は、文字列連結でもサポートされています。

"hello, " + "world"  // "hello, world"に等しい

2.4. 剰余演算子

剰余演算子(a % b)は、bの倍数がaの中にいくつ収まるかを計算し、残った値(剰余と呼ばれます)を返します。

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

剰余演算子のしくみは次のとおりです。 9 % 4を計算するには、最初に9の中にいくつの4が収まるかを計算します。

9の中に2つの4を収めることができ、残りは1です(オレンジ色で表示)。

Swiftでは、これは次のように記述されます。

9 % 4    // 1に等しい

a % bの答えを決定するために、%演算子は次の方程式を計算し、剰余を出力として返します。

a =(b x いくつかの乗数)+剰余

ここで、いくつかの乗数は、a内に収まるbの倍数の最大数です。

この方程式に9と4を挿入すると、次のようになります。

9 =(4 x 2)+ 1

次の負の値の剰余を計算する場合も、同じ方法が適用されます。

-9 % 4 // -1に等しい

方程式に-9と4を挿入すると、次のようになります。

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

剰余値を-1にします。

bが負の値の場合、bの符号は無視されます。これは、% bと% -bが常に同じ答えを与えることを意味します。

2.5. 単項マイナス演算子

単項プラス演算子(+)は、変更せずに、操作する値を返すだけです。

let minusSix = -6
let alsoMinusSix = +minusSix  // alsoMinusSixは、-6に等しい

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

2.6. 複合代入演算子

Cと同様に、Swiftは、代入(=)を別の演算と組み合わせる複合代入演算子を提供します。 一例は、加算代入演算子(+=)です。

var a = 1
a += 2
// aは3に等しい

式a += 2は、a = a + 2の省略形です。事実上、加算と割り当ては、両方のタスクを同時に実行する1つの演算子に結合されます。

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

Swift標準ライブラリによって提供される演算子については、演算子宣言を参照してください。

2.7. 比較演算子

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

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

注意
Swiftは、2つのID演算子(===と!==)も提供します。これを使用して、2つのオブジェクト参照が両方とも同じオブジェクトインスタンスを参照しているかどうかをテストします。 詳細については、「ID演算子」を参照してください。

各比較演算子は、ステートメントが真であるかどうかを示すブール値を返します。

1 == 1   // 1は1に等しいため、true
2 != 1   // 2は1に等しくないため、true
2 > 1    // 2は1より大きいため、true
1 < 2    // 1は2未満であるため、true
1 >= 1   // 1は1以上であるため、true
2 <= 1   // 2は1以上であるため、false

比較演算子は、ifステートメントなどの条件ステートメントでよく使用されます。

let name = "world"
if name == "world" {
    print("hello, world")
} else {
    print("I'm sorry \(name), but I don't recognize you")
}
// 名前は確かに「world」と等しいため、「hello、world」を出力します。

ifステートメントの詳細については、制御フローを参照してください。

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

(1, "zebra") < (2, "apple")   // 1は2未満であるため、true; 「ゼブラ」と「アップル」は比較されません
(3, "apple") < (3, "bird")    // 3は3に等しく、 "apple"は "bird"よりも小さいため、true
(4, "dog") == (4, "dog")      // 4は4に等しく、 "dog"は "dog"に等しいため、true

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

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

("blue", -1) < ("purple", 1)        // OK、trueと評価されます
("blue", false) < ("purple", true)  // <はブール値を比較できないためエラー

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

2.8. 三項条件演算子

三項条件演算子は、question ? answer1 : answer2の形式をとる3つの部分からなる特別な演算子です。これは、questionが正しいか間違っているかに基づいて2つの式のいずれかを評価するためのショートカットです。 questionがtrueの場合、answer1を評価し、その値を返します。それ以外の場合は、answer2を評価し、その値を返します。

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

if question {
    answer1
} else {
    answer2
}

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

let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeightは、90に等しい

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

let contentHeight = 40
let hasHeader = true
let rowHeight: Int
if hasHeader {
    rowHeight = contentHeight + 50
} else {
    rowHeight = contentHeight + 20
}
// rowHeightは、90に等しい

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

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

2.9. nil合体演算子

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

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

a != nil ? a! : b

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

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

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

let defaultColorName = "red"
var userDefinedColorName: String?   // デフォルトはnil

var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorNameは、nilであるため、colorNameToUseは、デフォルトの「赤」に設定されます

userDefinedColorName変数は、オプショナルの文字列として定義され、デフォルト値はnilです。userDefinedColorNameはオプショナル型であるため、nil合体演算子を使用してその値を検討できます。上記の例では、演算子を使用して、colorNameToUseという文字列変数の初期値を決定しています。userDefinedColorNameはnilであるため、式userDefinedColorName ?? defaultColorNameは、defaultColorNameの値または「red」を返します。

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

2.10. 範囲演算子

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

2.10.1. 閉範囲演算子

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

閉範囲演算子は、for-inループなど、すべての値を使用する範囲を反復処理する場合に役立ちます。

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

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

2.10.2. ハーフオープン範囲演算子

ハーフオープン範囲演算子(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])")
}
//人1はアンナと呼ばれます
//人2はアレックスと呼ばれます
//人3はブライアンと呼ばれます
//人4はジャックと呼ばれます

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

2.10.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

2.11. 論理演算子

論理演算子は、ブール論理値trueおよびfalseを変更または結合します。Swiftは、Cベースの言語に見られる3つの標準論理演算子をサポートしています。

  • 論理否定(!a)
  • 論理積(a && b)
  • 論理OR(a || b)

2.11.1. 論理NOT演算子

論理NOT演算子(!a)は、ブール値を反転して、trueがfalseになり、falseがtrueになるようにします。

論理NOT演算子はプレフィックス演算子であり、操作する値の直前に空白なしで表されます。次の例に示すように、「not a」と読み取ることができます。

let allowedEntry = false
if !allowedEntry {
    print("ACCESS DENIED")
}
// 「ACCESSDENIED」を出力します

if !allowedEntryフレーズは、「エントリが許可されていない場合」と読むことができます。次の行は、「許可されていないエントリ」がtrueの場合にのみ実行されます。つまり、allowedEntryがfalseの場合です。

この例のように、ブール定数と変数名を注意深く選択すると、二重否定や混乱する論理ステートメントを回避しながら、コードを読みやすく簡潔に保つのに役立ちます。

2.11.2. 論理AND演算子

論理AND演算子(a && b)は、式全体が真になるために両方の値が真でなければならない論理式を作成します。

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

この例では、2つのブール値を考慮し、両方の値が真の場合にのみアクセスを許可します。

let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// 「ACCESSDENIED」を出力します

2.11.3. 論理OR演算子

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

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

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

let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
//「Welcome!」を出力します

2.11.4. 論理演算子の組み合わせ

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

if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
//「Welcome!」を出力します

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

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

enterDoorCode、passedRetinaScan、hasDoorKeyの値に基づいて、最初の2つの部分式はfalseです。ただし、緊急オーバーライドパスワードは既知であるため、複合式全体は引き続きtrueと評価されます。

注意
Swift論理演算子&&および|| は左結合です。つまり、複数の論理演算子を持つ複合式は、左端の部分式を最初に評価します。

2.11.5. 明示的な括弧

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

if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
//「Welcome!」を出力します

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

タイトルとURLをコピーしました