Swift/文法
制御構造
[編集]Swiftの制御構造には、以下のような特徴があります。
- 括弧の必要性の低さ
- 条件式を括弧で囲む必要がない場合があります。たとえば、if文やwhile文の条件式に括弧は必要ありません。ただし、可読性の向上のために括弧を使用することが推奨されます。
- 波括弧の必要性
- C言語のように波括弧で複数の文をグループ化する必要があります。1つの文だけであっても波括弧を使用することが推奨されています。
- else ifの代わりにelse ifの連鎖
- C言語のように、else ifを使って複数の条件分岐を連鎖させることができます。ただし、Swiftでは、else ifではなく、elseとifを連結して連鎖することができます。これを「if文の連鎖」と呼びます。
- switch文の豊富な機能
- switch文は、多数のパターンマッチングの機能を提供しています。case節では、リテラル、範囲、タプル、型などのパターンを使用できます。また、Swift 2.0からは、@available、@objc、@IBOutletなどの属性にもマッチングすることができるようになりました。
- guard文の導入
- Swift 2.0から導入されたguard文は、条件が満たされない場合にすぐに抜けることができます。これは、エラー処理のために便利です。
分岐文
[編集]Swiftは、#if文、#guard文、#switch文 の3種類の分岐文( Branch Statements )を持ちます。
- 構文(EBNF)
branch-statement = if-statement | guard-statement | switch-statement
- 分岐文に関連する部分を抜粋[1]
if文
[編集]Swiftのif
文は、与えられた条件に従って処理を切り替えます。
- 構文(EBNF)
if-statement = "if" condition-list code-block [ "else" ( if-statement | code-block ) ] condition-list = condition [ "," condition-list ] condition = expression | availability-condition | case-condition | optional-binding-condition availability-condition = "#available" ( availability-arguments ) | "#unavailable" ( availability-arguments ) case-condition = "case" pattern initializer optional-binding-condition = "let" pattern [ initializer ] | "var" pattern [ initializer ] code-block = "{" [ statements ] "}"
- if文に関連する部分を抜粋[2]
- 形式
if 条件式1 { 文1 } else if 条件式2 { 文2 } else { 文3 }
- else節は省略可能です。
- else節を省略しなかった場合、else に if文あるいは単純なコードブロックが後続します。
- コード例
let a = 3 if a < 5 { print ("5未満です") } else { print ("5以上です") }
- 実行結果
5未満です
条件が Optional束縛 な if
[編集]Swiftのif文では、条件式にBool型の値を渡します。ただし、Optional型を使用して条件を指定することもできます。Optional型は、値が存在する場合は非Optional型を保持し、値が存在しない場合はnilを保持します。
if文の条件式にOptional型を指定した場合、そのOptional型が値を持っているかどうかを判定することができます。Optional型がnilを保持している場合、条件式はfalseとして扱われます。Optional型が値を持っている場合、条件式はtrueとして扱われます。
以下は、Optional型を使用したif文の例です。
- コード例
let optionalValue: Int? = 5 if let value = optionalValue { print("value is \(value)") } else { print("optionalValue is nil") }
上記の例では、optionalValueがOptional型であり、値を持っている場合には、valueにその値を束縛します。Optional型がnilを保持している場合には、elseブロックが実行されます。
#availableディレクティブと#unavailableディレクティブ
[編集]Swiftにおいて、#availableと#unavailableディレクティブを使用して、特定のプラットフォームのバージョンでのみ利用可能なコードを記述することができます。これにより、実行時にクラッシュする可能性のあるコードを回避できます。
#availableディレクティブ
[編集]- availableディレクティブは、実行するコードが特定のプラットフォームのバージョンで利用可能であることを確認するために使用されます。
これは、iOS、macOS、watchOS、tvOSなどの特定のバージョンに依存するAPIの使用をチェックするのに便利です。
以下は、iOS 15で新しく追加されたasync/awaitを使用する場合の例です。
- コード例
if #available(iOS 15.0, *) {
// async/await code here
} else {
// handle older iOS versions here
}
#unavailableディレクティブ
[編集]- unavailableディレクティブは、実行するコードが特定のプラットフォームのバージョンで利用できないことを確認するために使用されます。
例えば、iOS 14で廃止されたAPIを使用する場合、以下のように書くことができます。
- コード例
if #unavailable(iOS 14.0, *) { // handle iOS 14 deprecated API here } else { // use current API here }
- availableおよび#unavailableディレクティブは、if文、guard文、switch文の中で使用できます。
guard文
[編集]Swiftのguard
文は、条件式がfalse
の場合に、else
節で定義された処理を実行する文法です。guard
文は主に、事前条件を確認して、前提条件を満たさない場合はエラーを投げるか、処理を中断する場合に使用されます。
guard
文のelse
節では、return
文やthrow
文を使って、処理を中断させる必要があります。これは、else
節内で定義された変数や定数が、guard
文の外側でも有効になっているためです。
また、guard
文を使うことで、if-let
文などよりも、コードの可読性を高く保ちながら、エラー処理やオプショナルバインディングを実行することができます。
- 構文(EBNF)
guard-statement = "guard" condition-list "else" code-block condition-list = condition [ "," condition-list ] condition = expression | availability-condition | case-condition | optional-binding-condition availability-condition = "#available" ( availability-arguments ) | "#unavailable" ( availability-arguments ) case-condition = "case" pattern initializer optional-binding-condition = "let" pattern [ initializer ] | "var" pattern [ initializer ] code-block = "{" [ statements ] "}"
- if文に関連する部分を抜粋[3]
- 形式
guard 条件式 else { 条件を満たさなかった場合の処理。 このコードブロックではスコープを抜ける処理が必須 }
- 例
func intToUInt(_ n: Int) -> UInt { guard n >= 0 else { print("引数が0未満のため、正しく変換できませんでした。") return 0 } return UInt(n) } for num in [1, 0, -3] { print(intToUInt(num)) }
- 実行結果
1 0 引数が0未満のため、正しく変換できませんでした。 0
- guard文の条件にはOptional bind condが使え、条件が偽(=nil)だと else に続くコードブロックが実行され(関数を終了し)ます。
- 条件が真(nil以外)だと、guard文で宣言された識別子はアンラップされguard文を抜けたあとも参照できます。
switch文
[編集]- 構文(EBNF)
switch-statement = "switch" expression "{" [ switch-cases ] "}" switch-cases = switch-case [ switch-cases ] switch-case = case-label statements | default-label statements | conditional-switch-case case-label = [ attributes ] "case" case-item-list ":" case-item-list = pattern [ where-clause ] | pattern [ where-clause ] "," case-item-list default-label = [ attributes ] "default" ":" where-clause = "where" where-expression where-expression = expression conditional-switch-case = switch-if-directive-clause [ switch-elseif-directive-clauses ] [ switch-else-directive-clause ] endif-directive switch-if-directive-clause = if-directive compilation-condition [ switch-cases ] switch-elseif-directive-clauses = elseif-directive-clause [ switch-elseif-directive-clauses ] switch-elseif-directive-clause = elseif-directive [ compilation-condition switch-cases ] switch-else-directive-clause = else-directive [ switch-cases ] if-directive = "#if" elseif-directive = "#elseif" else-directive = "#else" endif-directive = "#endif"
- switch文に関連する部分を抜粋[4]
- 形式
switch 値 { case パターン1: println("case 1") // 暗黙的にbreakされる case パターン2, パターン3: //複数条件 print("case 2 or 3") case パターン4 where 条件式: // where 条件式でパターンに制限を加える事が出来る print("case 4") fallthrough // フォールスルー default: print("default") }
- 複数のfallthroughを含むSwitch文
let a = 3 switch a { case 0: print("0です") case 1: print("1です") case 3: print("3です") fallthrough case 2, 3: print("2または3です") fallthrough case 2 ... 10: print("2 ... 10 にマッチしました") default: print("どれでもありません") }
- 表示結果
2または3です 3 ... 10 にマッチしました 3です
- Switch 文は、Case 節のパターンに一致した場合、対応する処理を実行します。
- このとき、C言語ファミリーのSwitch文と異なり、処理が終わると Switch文を終了し以降のCase節は評価されません。
- また、キーワード
fallthrough
は、強制的に次の処理を継続するだけで、再度、値を評価するものではありません。- この様にパターンを超えた制御の継続をフォールスルーと呼びます。
let a = 1 switch a { case 0: print("0です") case 1: print("1です") fallthrough // フォールスルー case 1, 2, 3: print("1 または 2または3です") default: print("どれでもありません") }
- 表示結果
1です 1 または 2または3です
- パターンによる値束縛
let value = (404, "Not Found", false) switch value { case (200...299, _, false), (300...499, _, true): print("(※注意 ステータスコードと成否に不整合があります。)") fallthrough case (_, _, true): print("成功") case (404, _, false): print("失敗: ファイルが見つかりませんでした") case (let status, let message, false): print("失敗: \(message) (\(status))") default: print("未知のステータス") }
- 表示結果
失敗: ファイルが見つかりませんでした
- Enumに対するSwitch文
enum RPS: CaseIterable { case Rock case Paper case Scissors } let rps = RPS.Rock switch rps { case .Rock: print("グー") case .Paper: print("パー") case .Scissors: print("チョキ") } print(rps) print(RPS.allCases)
- 表示結果
グー Rock [Main.RPS.Rock, Main.RPS.Paper, Main.RPS.Scissors]
- Swift 4.2からは CaseIterable プロトコルが使えます。
- CaseIterable を使うと従来のように追加のコードを書くことなく、.allCases で列挙型の全要素の集合を得ることができます。
case _:
は、default :
と同じ意味になります。
- ASSOC VAL をつかわないでメソッドを追加する方法
enum RPS : CaseIterable { case Rock case Paper case Scissors func string() -> String { switch self { case .Rock: return "グー" case .Paper: return "パー" case .Scissors: return "チョキ" } } } RPS.allCases.forEach { rps in print("\(rps) => \(rps.string())") }
- 表示結果
Rock => グー Paper => パー Scissors => チョキ
CaseIterable
[編集]CaseIterable
は、Swiftのプロトコルで、列挙型を定義するときに実装することができます。このプロトコルを実装することで、列挙型のケースを配列として取得できるようになります。
例えば、以下のようなFruit
という列挙型があるとします。
enum Fruit { case apple case banana case orange }
この列挙型にCaseIterable
プロトコルを実装することで、以下のように全てのケースを配列として取得できます。
enum Fruit: CaseIterable { case apple case banana case orange } let allFruits = Fruit.allCases // [Fruit.apple, Fruit.banana, Fruit.orange]
このように、CaseIterable
を使うことで、網羅性をチェックすることができます。網羅性とは、すべての可能性をカバーしているかどうかを表します。例えば、以下のような関数があったとします。
func printFruit(_ fruit: Fruit) { switch fruit { case .apple: print("Apple") case .banana: print("Banana") } }
この関数は、Fruit
列挙型のすべてのケースを網羅していません。しかし、Fruit
にCaseIterable
プロトコルを実装した上で、以下のように修正することで、すべてのケースを網羅することができます。
func printFruit(_ fruit: Fruit) { switch fruit { case .apple: print("Apple") case .banana: print("Banana") case .orange: print("Orange") } }
このように、CaseIterable
を使うことで、列挙型のすべてのケースを配列として取得でき、網羅性をチェックすることができます。
パターン
[編集]Swiftの「パターン」とは、値を分解して特定の部分を取り出すために使用される仕組みです。 パターンを使用することで、Swiftは値をより正確に特定し、コードの可読性を向上させることができます。
以下では、主要なパターンについて解説します。
ワイルドカードパターン
[編集]ワイルドカードパターンは、アンダースコア(_)で表されます。このパターンは、値を特定せずに、単にマッチさせたい場合に使用されます。たとえば、以下のように使用できます。
let x = 5 switch x { case _: print("マッチしました") }
このコードでは、xがどの値でも、ワイルドカードパターンにマッチしています。
識別子パターン
[編集]識別子パターンは、値が識別子として解決できる場合に使用されます。たとえば、変数名や定数名が識別子パターンになります。
let x = 5 switch x { case let y: print("yは\(y)です") }
このコードでは、識別子パターンがlet yであり、yがマッチする値をバインドします。
列挙型ケースパターン
[編集]列挙型ケースパターンは、列挙型の値をマッチさせるために使用されます。例えば、以下のように使用できます。
enum Color { case red case green case blue } let color = Color.red switch color { case .red: print("赤色") case .green: print("緑色") case .blue: print("青色") }
このコードでは、列挙型ケースパターンが.redであり、colorがColor.redとマッチすることになります。
オプショナルパターン
[編集]オプショナルパターンは、オプショナル型の値をマッチさせるために使用されます。たとえば、以下のように使用できます。
let optionalValue: Int? = 5 switch optionalValue { case .some(let x): print("値は\(x)です") case .none: print("値はありません") }
このコードでは、オプショナルパターンが.some(let x)であり、optionalValueが値を持つ場合は、その値がxにバインドされます。
式パターン
[編集]式パターンは、式と一致するかどうかをテストします。例えば、変数の値が特定の範囲内にあるかどうかをテストする場合に使用できます。以下は、式パターンの例です。
let value = 5 switch value { case 0: print("value is zero") case 1...5: print("value is between 1 and 5") default: print("value is greater than 5") }
この例では、valueの値が1〜5の範囲にある場合は2番目のケースが実行されます。式パターンは、範囲演算子を使用することができるため、数値や文字列などの比較に特に便利です。
タイプキャスティングパターン
[編集]タイプキャスティングパターンは、オブジェクトの型と一致するかどうかをテストします。タイプキャスティングパターンは、is演算子を使用してテストすることができます。以下は、タイプキャスティングパターンの例です。
class Animal { } class Dog: Animal { } class Cat: Animal { } let animals: [Animal] = [Dog(), Cat(), Dog(), Cat()] for animal in animals { switch animal { case is Dog: print("It's a dog") case is Cat: print("It's a cat") default: print("It's something else") } }
この例では、animals配列の要素がDog型かCat型かをテストしています。is演算子を使用することで、Dog型かCat型かどうかを簡単にテストすることができます。
繰返し文
[編集]Swiftは、#for-in文、#while文、#repeat-while文の3種類の繰返し文( Loop Statements )を持ちます。
- 構文(EBNF)
loop-statement = for-in-statement | while-statement | repeat-while-statement
- 繰返し文に関連する部分を抜粋[5]
for-in文
[編集]Swiftの for-in
文は、シーケンスやコレクションの要素を反復処理するために使用されます。
- 構文(EBNF)
for-in-statement = "for" [ "case" ] pattern "in" expression [ where-clause ] code-block
- for-in文に関連する部分を抜粋[6]
- 配列の要素を繰返し
let names = ["太郎", "花子", "一郎"] for name in names { print("\(name)さん、こんにちは!") }
- 実行結果
太郎さん、こんにちは! 花子さん、こんにちは! 一郎さん、こんにちは!
- 整数の範囲を繰返し
for index in 0..<5 { print("index: \(index)") }
- 実行結果
index: 0 index: 1 index: 2 index: 3 index: 4
- stride関数を対象に
for year in stride(from: 1900, to:2000, by:4) { print(year) }
- 実行結果(抜粋)
1900 1904 // 長いので中略 1988 1992 1996
- where句による条件指定
for index in 0..<10 where index % 2 == 1 { print(index) }
- 実行結果
1 3 5 7 9
- ラベル指定による多重ループからのbreak
let array = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3] var index1: Int? var index2: Int? loop1: for i in 0..<array.count { for j in (i + 1)..<array.count { if array[i] == array[j] { (index1, index2) = (i, j) break loop1 } } } print(index1) print(index2)
- 実行結果
Optional(0) Optional(9)
while文
[編集]Swiftのwhile
文は、指定された条件がfalse
になるまで、ブロック内のコードを実行し続けます。
while
ループは、条件がtrue
になるまで一度も実行されない場合があるため、最初から条件がfalse
であることがわかっている場合は、repeat-while
文を使う方が適しています。
- 構文(EBNF)
while-statement = "while" condition-list code-block condition-list = condition | condition , condition-list condition = expression | availability-condition | case-condition | optional-binding-condition availability-condition | ”#available” ( availability-arguments ) | "#unavailable" ( availability-arguments ) case-condition = "case" pattern initializer optional-binding-condition = "let" pattern [ initializer ] | "var" pattern [ initializer ]
- while文に関連する部分を抜粋[7]
- 形式
while 条件式 { 文 }
- Optional束縛
let array = [1, 2, 3, 4, 5] var arrayIterator = array.makeIterator() while let item = arrayIterator.next() { print("\(item)") }
repeat-while文
[編集]Swiftのrepeat-while
文は、指定された条件がtrueの場合に繰り返し処理を実行するための制御構文です。この文は、最低1回の実行を保証します。while
キーワードの後に条件が続き、実行が続けられるかどうかを判断します。
- 構文(EBNF)
repeat-while-statement = "repeat" code-block "while" expression
- while文に関連する部分を抜粋[8]
- 形式
repeat { 文 } while 条件式
条件式の書き方
[編集]条件式はBool
型かBool
型の式か、BooleanType
プロトコルを実装する型でなければなりません。
条件式を書くときに使う論理演算子や比較演算子の仕様は下記のとおり。
比較演算子
[編集]
|
|
|
print(1...5 ~= 3) // true
var str = "mission control" print(str.range(of: "control")! ~= str.index(of: "c")!) // true
論理演算子
[編集]文法 | 意味 |
---|---|
&& |
論理AND |
|| |
論理OR |
! |
論理NOT |
Optional束縛
[編集]条件式には、optional-binding-condition を取ることができます。 構文的には、変数宣言(あるいは定数宣言)に似ていますが、nilを取り得るOptional型で、コンディションリストの最後の式の値が条件式の評価対象になります。
if let kiwiIndex = find(fruits, "kiwi") { print("キウイは\(kiwiIndex!)番目に見つかりました") } else { print("キウイは見つかりませんでした") }
defer文
[編集]スコープを抜けた時に、自動的に実行される処理の登録。
func f() { print("a start") defer { print("a end") } print("b start") defer { print("b end") } } f()
- 実行結果
a start b start b end a end
- このように、実行は登録とは逆順になります。
do-try-catch文
[編集]throws
、rethrows
キーワードが指定されたメソッドは、try
、try?
、try!
のいずれかを指定してコールする必要があります。
#throwsキーワードも参照のこと。
import Foundation let decoder = JSONDecoder() let data = "[\"some string\", \"another string\"]".data(using: .utf8) do { // tryをdo節で囲むか、メソッド内であればメソッド自体にrethrowsキーワードを指定する必要がある let decoded = try decoder.decode([String].self, from: data!) // tryの場合、throwされたら対応するcatch節に処理が移る print(decoded) } catch let error { print(error) }
// 前略 let decoded = try? decoder.decode([String].self, from: data!) // try?の場合、throwされたら結果がnilになる print(decoded)
// 前略 let decoded = try! decoder.decode([String].self, from: data!) // try!の場合、throwされたら実行時エラーになる print(decoded)
関数とクロージャ
[編集]関数とクロージャは第一級関数であり、普通のオブジェクトと同じように変数に入れたり、関数の引数や戻り値として受け渡しできます。
関数
[編集]Swift の関数の構文は、おおむね下記のようになります。
func 関数名(引数名1: 型名1, 引数名2: 型名2) -> 戻り値の型 { // 付随する処理内容などあればここに書く return 戻り値 }
- 例
func calcBMI(weight: Double, height: Double) -> Double { return weight / (height * height) * 10000 }
外部引数名
[編集]上述のような構文で関数を書く場合、関数の呼び出し側では、関数の引数には外部引数名(external parameter name)を指定しなければなりません。下記コード例では5行目のweightが外部引数です。
対して関数内部のみで使用できる引数名はローカル引数名(local parameter names)と呼ぶ。下記コード1行目のweightがローカル引数です。
func calcBMI(weight w: Double, height h: Double) -> Double { return w / (h * h) * 10000 } var a = calcBMI(weight: 60, height: 160) // 23.4375 print(a)
- 実行結果
23.4375
関数の呼び出し側にて外部引数名を省略させるためには、宣言時の外部引数名に _ (アンダースコア)を記述します。単に記述しなかった場合、外部引数名とローカル引数名に同じ値を指定したものとみなされます。
func add(first:Int, second:Int) -> Int { return first + second } print(add(first: 2, second: 3)) // print(add(2, 3)) // 文法エラー
func add(_ first:Int, _ second:Int) -> Int { return first + second } // print(add(first: 2, second: 3)) // 文法エラー print(add(2, 3))
可変長引数
[編集]func maxInt(first: Int, rest: Int...) -> Int { var max = first for num in rest { if max < num { max = num } } return max } maxInt(first:7, rest:3, 5, 9, 2) // 9 maxInt(first:6) // 6
デフォルト引数
[編集]func sayHello(name: String = "world") { print("Hello, \(name)!") } sayHello() // Hello, world! sayHello(name: "Swift") // Hello, Swift!
in-out引数
[編集]func swapInts(a: inout Int, b: inout Int) { let tmp = a a = b b = tmp } var x = 42, y = 99 swapInts(&x, &y) print("x = \(x), y = \(y)") // x = 99, y = 42
throwsキーワード
[編集]- do/try/throw/catch を使ったエラーハンドリング
enum FactorialError: Error { case NegativeError case ArithmeticOverflowError } // 階乗を求める関数 // エラーをthrowする可能性あるのでthrows が必要 func factorial(of n:Int) throws -> Int { // nが負の数の場合例外を挙げる guard n >= 0 else { throw FactorialError.NegativeError } var result = 1 if n == 0 { return result } // 正しくオーバーフローを捕捉するため非再帰版 for i in 1...n { // 算術オーバーフロー発生時に例外を挙げる if result > Int.max / i { throw FactorialError.ArithmeticOverflowError } result *= i } return result } for i in -10...30 { do { let fact = try factorial(of: i) print("\(i)! = \(fact)") } catch FactorialError.NegativeError { print("Error: \(i) < 0") } catch FactorialError.ArithmeticOverflowError { print("Error: \(i)! is too big") } }
- 実行結果
Error: -10 < 0 Error: -9 < 0 Error: -8 < 0 Error: -7 < 0 Error: -6 < 0 Error: -5 < 0 Error: -4 < 0 Error: -3 < 0 Error: -2 < 0 Error: -1 < 0 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800 11! = 39916800 12! = 479001600 13! = 6227020800 14! = 87178291200 15! = 1307674368000 16! = 20922789888000 17! = 355687428096000 18! = 6402373705728000 19! = 121645100408832000 20! = 2432902008176640000 Error: 21! is too big Error: 22! is too big Error: 23! is too big Error: 24! is too big Error: 25! is too big Error: 26! is too big Error: 27! is too big Error: 28! is too big Error: 29! is too big Error: 30! is too big
拡張メソッド
[編集]Swiftには、既存の型に新しい機能を追加するExtension機能があります。
extension Int { func multiply(by number: Int) -> Int { return self * number } } let value = 5 let result = value.multiply(by: 3) // 結果は15 print(result)
- 実行結果
15
Int
型に対して新しい機能を追加しています。multiply
という名前のメソッドが定義されており、引数として別の整数number
を受け取り、自身の値(self
)とその引数の数値を掛け合わせて計算し、結果を返す機能を追加しています。value
という変数に整数値5
を代入し、その後、value
に対してmultiply(by: 3)
という拡張メソッドを呼び出しています。これにより、5
と3
を掛け合わせた結果である15
がresult
に代入されます。- 最後に、
result
の値である15
をコンソールに出力しています。
このように、SwiftのExtension機能を使用すると、既存の型に新しいメソッドを追加することができます。 これにより、標準ライブラリの型にカスタム機能を追加したり、コードの再利用性を高めたりすることができます。
クロージャ
[編集]Swiftでは中括弧{}
で囲まれたコードブロックは事実上全てクロージャとなります。
このとき、引数・戻り値と、本体の処理の間に「in
」を書くことで、この2つの部分を区別します。
[1, 2, 3].map({ (number: Int) -> Int in let result = 3 * number return result })
クロージャの型が推論可能な場合等では、各種省略記法を使用できます。
[1, 2, 3].map({ number -> Int in let result = 3 * number return result })
[1, 2, 3].map({ number in 3 * number })
関数の最後の引数がクロージャである場合、それを括弧の外に出すことができます。また、それが唯一の引数である場合は括弧そのものを省略できます。
[1, 2, 3].map(){ 3 * $0 }
[1, 2, 3].map{ 3 * $0 }
キャプチャリスト
[編集]クロージャでキャプチャする変数の宣言。weak
、unowned
を指定すると、保持されない参照になります。
import Foundation var closure1 : () -> () var closure2 : () -> () var closure3 : () -> () do { var str = "hello" as NSString closure1 = { print("str : \(str)") } closure2 = { [str] in print("str : \(str)") } closure3 = { [weak str] in print("str : \(str)") } str = "goodbye" } closure1() closure2() closure3() /* 実行結果: str : goodbye str : hello str : Optional(hello) */ // 補足: closure2に関する処理を削除すると、closure3の出力結果はnilになります(strを保持する参照が1つもなくなるため)
演算子
[編集]演算子オーバーロード
[編集]Swiftでは任意の型に対して、既存の演算子をオーバーロードして独自の振る舞いを定義することができます。
なお =
->
.
//
/*
*/
は予約されておりオーバーロードしたりカスタム演算子(後述)として定義することはできません。
- 演算子オーバーロード
struct Vector2D { var x = 0.0 var y = 0.0 } func + (lhs: Vector2D, rhs: Vector2D) -> Vector2D { return Vector2D(x: lhs.x + rhs.x, y: lhs.y + rhs.y) } prefix func - (rhs: Vector2D) -> Vector2D { return Vector2D(x: -rhs.x, y: -rhs.y) } func += (lhs: inout Vector2D, rhs: Vector2D) { lhs = lhs + rhs } prefix func ++ (rhs: inout Vector2D) -> Vector2D { rhs += Vector2D(x: 1.0, y: 1.0) return rhs } let vec1 = Vector2D(x: 2.0, y: 3.0) let vec2 = Vector2D(x: 5.0, y: 1.0) print("let vec1 = Vector2D(x: 2.0, y: 3.0)") print("let vec2 = Vector2D(x: 5.0, y: 1.0)") print("vec1 ⇒ \(vec1)") print("vec2 ⇒ \(vec2)") print("vec1 + vec2 ⇒ \(vec1 + vec2)") var vec3 = -vec1 print("var vec3 = -vec1") print("vec3 ⇒ \(vec3)") vec3 += vec2 print("vec3 += vec2") print("vec3 ⇒ \(vec3)") print("++vec3 ⇒ \(++vec3)")
- 実行結果
let vec1 = Vector2D(x: 2.0, y: 3.0) let vec2 = Vector2D(x: 5.0, y: 1.0) vec1 ⇒ Vector2D(x: 2.0, y: 3.0) vec2 ⇒ Vector2D(x: 5.0, y: 1.0) vec1 + vec2 ⇒ Vector2D(x: 7.0, y: 4.0) var vec3 = -vec1 vec3 ⇒ Vector2D(x: -2.0, y: -3.0) vec3 += vec2 vec3 ⇒ Vector2D(x: 3.0, y: -2.0) ++vec3 ⇒ Vector2D(x: 4.0, y: -1.0)
カスタム演算子
[編集]Swift標準の定義済み演算子(+
、*
、%
など)の他に、全く新しい演算子を定義することができます。新しく定義したカスタム演算子は、標準の演算子と同じように演算子オーバーロードを実装できます。
- 冪乗演算子の定義
import Foundation precedencegroup Exponentiative { associativity: right higherThan: MultiplicationPrecedence } infix operator **: Exponentiative func ** (lhs: Double, rhs: Double) -> Double { return pow(lhs, rhs) } print(2 ** 3) print(0.5 * 2 ** 3 ** 2) print(0.5 * (2 ** (3 ** 2)))
- 実行結果
8.0 256.0 256.0
- pow() を使うために
- precedencegroup は演算子の特性を定義します
- associativity は結合方向
- 冪乗演算子の結合方向数学でもプログラミングでも流派が別れますが、今回は右で
- 演算子の優先順位は乗除算より上にしました。
- 冪乗は中置なので infix です。演算子のトークン
**
と特性 Exponentiative を併せて指定しています。- ほかに、前置:prefix 後置:postfix があります
- 関数の実装です。トークンが関数名になります。
脚註
[編集]- ^ Summary of the Grammar -- Branch Statements
- ^ Summary of the Grammar
- ^ Summary of the Grammar -- guard-statement
- ^ Summary of the Grammar -- switch-statement
- ^ Summary of the Grammar -- Loop Statements
- ^ Summary of the Grammar -- For-In Statements
- ^ Summary of the Grammar -- While Statements
- ^ Summary of the Grammar -- Repeat-While Statement