Swift
Swiftは、Appleが開発したプログラミング言語で、iOS、macOS、watchOS、tvOSアプリケーションの開発に主に使用されます。2014年に発表され、モダンな構文、安全性、パフォーマンスを重視して設計されています。オープンソースで、初心者にも学びやすく、Objective-Cの代替として広く採用されています。
目次
[編集]- Swift/
- Swift/文法 - 主に制御構造、関数定義について
- Swift/オブジェクト指向 - 主に列挙体、クラス、構造体について
- Swift/非同期処理 - 主にGrand Central Dispatchによる非同期処理について
環境準備
[編集]オンラインコンパイル実行
[編集]ローカルにコンパイル実行環境を作るのが困難、すぐにコンパイルと実行を試してみたい。そんな場合には、オンラインコンパイル実行環境を使うことも検討に値します。
- Swift Playgrounds
- 公式のオンラインコンパイル実行環境です。
- paiza.io
- 複数のプログラミン言語に対応しており、Swiftにも対応しています。
ビルド済みバイナリーの導入
[編集]公式サイトの、Install Swiftからインストーラーを入手するのが基本です。
iPadOS
[編集]Appleから、「Swift Playgrounds」が学生を対象に公開されています。 Swift Playgroundsは開発環境であるとともに、Swiftの基本を習得するためのチュートリアルを含んでいます。
macOS
[編集]Swiftを開発したAppleのプラットフォームです。 Xcodeに統合されています。 複数のツールチェインがSwiftをサポートしているので、目的に応じて切り替えて使用します。
Windows
[編集]Windows10以降をサポートしています(Windows8.1以前はサポート対象外)。swift公式サイトではwingetなどパッケージマネージャを用いてインストールすることを推奨している。
GNU/Linuxのディストリビューション
[編集]公式サイトの、Install Swiftからインストールします。
公式サイトが対応している Linux platform:
- Amazon Linux
- Debian
- Fedora
- Red Hat
- Ubuntu
ソースコードからのビルド
[編集]最新のあるいはリリース前のバージョンのSwiftをインストールしたい場合は、ソースコードを入手しビルドします。
- ソースコードの入手
- https://github.com/apple/swift から clone します。
- ビルド環境の構築
- ビルド環境の構築のために build-toolchain スクリプトが用意されています。--help で全てのオプションの説明を見ることが出来ます。
- コンフィギュレーション
- ビルド
- テスト
- インストール
バージョン確認コマンド
[編集]インストールに成功したと思ったら、バージョン確認など簡単なコマンドを実行してみましょう。
swift --version
で確認できます。
実行結果の一例(WindowsのPowershellから実行)
PS C:\Users\ユーザー名> swift -version compnerd.org Swift version 5.8.1 (swift-5.8.1-RELEASE) Target: x86_64-unknown-windows-msvc
実行方法
[編集]Hello World
[編集]コードは
- hello.swift
print( "Hello, World!" )
の1行です。これをテキストエディターでコードを書き、ファイル名 hello.swift
保存します。swiftのソースファイルの拡張子には .swift
が使われます。
コンパイルする場合
[編集]実行方法は、まずコンパイルのために
$ swiftc ファイル名.swift
を実行します。
すると、標準設定ならカレントディレクトリなどシステム標準の場所に ファイル名 の実行ファイルが出来るので、あとはそれを通常の実行ファイルと同じ方法で実行します。
MS-DOSやWindowsでは、PATHが通っていなくてもカレントフォルダのファイルを名前を打つだけで実行できますが、他のプラットフォームの場合は
$ ./ファイル名
のようなに ./
を前置してカレントディレクトのファイルであることを明示して実行します。
インタプリタの場合
[編集]コマンド
$ swift ファイル名.swift
で、コンパイルと実行を行えます。
かつては「swift run ファイル名.swift」というコマンドでしたが、いまでは廃止され、上のようなコマンドになっています。
対話モードの場合
[編集]$ swift
で対話モードになります。
終了したい場合、Ctrl + D ボタンで終了します。
定数と変数
[編集]変数
[編集]変数は、値と結びついた識別子で初期化や代入によって結びつく値を変えることが出来ます。
変数は、キーワード var
を使って宣言します。
- 変数の宣言
var 変数名: 型 = 式
定数
[編集]定数は、一度しか値を初期化あるいは代入できない変数です。
定数は、キーワード let
を使って宣言します。
- 定数の宣言
let 定数名: 型 = 式
- 変数の値を参照
var a: Int = 47 print( a )
- 実行結果
47
型 Int
は冒頭が大文字でなければなりません。Swiftでは大文字と小文字は区別されます。
識別子
[編集]定数や変数の名前のことを識別子( identifier )と言います(他にも関数・メソッド・クラス・クラスのメンバーなどの名前も識別子で、同じ名前空間の中では識別子は重複できません)。
- 識別子には以下の文字セットが使えます。
- 英数字(a-z, A-Z, 0-9)
- アンダースコア(_)
- $ 記号
- Unicode文字
- 識別子の先頭に数字(0-9)を使うことは出来ません。
var
のようなSwiftのキーワードは識別子に使用できません。print
のような標準ライブラリー関数は識別子に使用すべきではありません。
この規則に違反したコードはコンパイルエラーになります。
- 標準ライブラリー関数の名前を定数の名前に使った
let print = 0 print(print)
- コンパイル結果
main.swift:2:1: error: cannot call value of non-function type 'Int' print(print)
ただし、識別子を `(バッククオーテーション)で括るとキーワードも識別子に使うことができます。
- `(バッククオーテーション)で括りキーワードを識別子に使う
let `var` = 42 print(`var`)
- 実行結果
42
初期化と参照
[編集]- 定数と変数は宣言時の初期化を省略できます
var a: Int a = 31 print( a ) let b: Int b = 42 print( b )
- 実行結果
31 42
定数と変数は宣言時に初期化を省略できますが、初期化を省略した変数の値を参照するとコンパイル時にエラーになります。
- 宣言時に初期化を省略した変数の値を参照(コンパイル時にエラーになります)
var a: Int print( a )
- コンパイル結果
main.swift:2:8: error: variable 'a' used before being initialized print( a ) ^
型推論
[編集]初期値を伴って変数か定数が宣言された場合、型アノテーション(type annotation; : 型名
の部分)を省略することができます。
これを型推論( type inference )と呼びます。
- 型推論
let a = 31 print( a, String(describing: type(of: a)) ) let b = "abc" print( b, String(describing: type(of: b)) ) let c = 3.1415926536 print( c, String(describing: type(of: c)) )
- 実行結果
31 Int abc String 3.1415926536 Double
String(describing: type(of: 式)
は、式の型を文字列で返します。
一般に定数と変数は宣言に初期化することは良い習慣だと考えられています。このため、多くのシチュエーションで型推定が可能となり型アノテーションが省略できます。
型アノテーションを省略すると、『コードの意図が読みにくくなる』とコードレビューで指摘されることがありますが、全部の宣言に型アノテーションを追加すると、『型アノテーションが冗長』とコードレビュー指摘されます。「どのような場合に、型アノテーションを書くべきか」一般則はありませんが let str = "abc"
に型アノテーションを書くべきだと主張する人はまれなので、定数と変数の名前えらびは重要で、単に a や b の様な「色のない」 名前は選ぶべきではありません。
暗黙の型変換は行われない
[編集]なお、いかなる数値型の変数についても暗黙の型変換( implicit conversion )が行われることはなく、型の違う数値同士の演算、右辺と左辺で型の異なる代入は全てコンパイルエラーとなります。
- 符号の有無の違う定数による初期化
let signed: Int = 1 let unsigned: UInt = signed
- コンパイル結果
main.swift:2:22: error: cannot convert value of type 'Int' to specified type 'UInt' let unsigned: UInt = signed ^~~~~~ UInt( )
- 数値リテラル
1
は、UIntの範囲内ですが swift は型の不整合を理由にエラーにします。
- 型の異なる定数と数値リテラルからなる式
let signed: Int = 1 let number = signed + 1.0
- コンパイル結果
main.swift:2:8: error: binary operator '+' cannot be applied to operands of type 'Int' and 'Double' signed + 1.0 ~~~~~~ ^ ~~~ main.swift:2:8: note: overloads for '+' exist with these partially matching parameter lists: (Double, Double), (Int, Int) signed + 1.0 ^
- 型の異なる数値リテラル同士からなる式
let number = 1 + 1.0 print( number, String(describing: type(of: number)) )
- 実行結果
2.0 Double
- これは暗黙の型変換に似ていますが、数値リテラルのみからなる式(=コンパイル時定数式)で、コンパイル時に静的に評価されます。
- Double.piは数値リテラル扱い?
let rad_to_deg_factor = Double.pi / 180 print( rad_to_deg_factor, String(describing: type(of: rad_to_deg_factor)) )
- 実行結果
0.017453292519943295 Double
- なぜ?
基本データ型
[編集]Swiftには、整数、浮動小数点数、真理値型、文字列型などの基本的なデータ型があります。これらのデータ型は、Swiftのプログラミングにおいて非常に重要な役割を果たします。本稿では、Swiftの基本データ型について、その特徴や使い方について解説していきます。
整数
[編集]以下はSwiftにおける整数とそのバリエーションとリテラルに関する表です。
タイプ 範囲 大きさ リテラルの例 Int8 (符号付き)
-128 〜 127 8ビット let myInt8: Int8 = -12
UInt8 (符号なし)
0 〜 255 8ビット let myUInt8: UInt8 = 42
Int16 (符号付き)
-32,768 〜 32,767 16ビット let myInt16: Int16 = -1234
UInt16 (符号なし)
0 〜 65,535 16ビット let myUInt16: UInt16 = 5678
Int32 (符号付き)
-2,147,483,648 〜 2,147,483,647 32ビット let myInt32: Int32 = -123456
UInt32 (符号なし)
0 〜 4,294,967,295 32ビット let myUInt32: UInt32 = 123456
Int64 (符号付き)
-9,223,372,036,854,775,808 〜 9,223,372,036,854,775,807 64ビット let myInt64: Int64 = -1234567890
UInt64 (符号なし)
0 〜 18,446,744,073,709,551,615 64ビット let myUInt64: UInt64 = 1234567890
Int (符号付き)
-2,147,483,648 〜 2,147,483,647 (32ビット または 64ビット)
32ビットまたは64ビット (環境による)
let myInt: Int = -1234567890
UInt (符号なし)
0 〜 4,294,967,295 (32ビット または 64ビット)
32ビットまたは64ビット (環境による)
let myUInt: UInt = 1234567890
Swiftには、整数のリテラルに使用できる多くのオプションがあります。以下に、Swiftで使用できる整数のリテラル構文の例を示します。
let decimalInt = 17 // 10進数 let binaryInt = 0b10001 // 2進数 let octalInt = 0o21 // 8進数 let hexInt = 0x11 // 16進数
これらのリテラルの前には、マイナス記号を使用して、負の整数を表すことができます。また、アンダースコア(_)を使用して、リテラルの読みやすさを向上させることができます。
浮動小数点数
[編集]以下はSwiftの浮動小数点数とそのバリエーション、およびリテラルの表です。
データ型 ビット 最小値 最大値 リテラル Float
32 1.175494e-38 3.402823e+38 3.14
,0.1e2
,1.25E-2
Double
64 2.2250738585072014e-308 1.7976931348623157e+308 3.14
,0.1e2
,1.25E-2
Float80
80 3.36210314311209350626e-4932 1.18973149535723176505e+4932 3.14
,0.1e2
,1.25E-2
以下はSwiftのコード例です。
let a: Float = 3.14 let b: Double = 0.1e2 let c: Float80 = 1.25E-2 print(a) // 3.14 print(b) // 10.0 print(c) // 0.0125
真理値
[編集]Swiftでは、真を表すのにtrue、偽を表すのにfalseを使用します。これらは、Boolというデータ型の値です。例えば、以下のように使用します。
let happy: Bool = true let hungry = false print(happy) // true print(hungry) // false
文字と文字列
[編集]以下はSwiftで使用可能な文字型、バリエーション、およびリテラルの表です:
型 バリエーション リテラルの例 Character Unicodeスカラー値 "A"
String Unicodeスカラー値または文字列 "Hello, world!"
こちらはSwiftでの文字型の実際のコード例です:
let letterA: Character = "A" let message: String = "Hello, world!"
文字列と文字列の連結
[編集]以下はSwiftの文字列と文字列の連結の方法です。まず、+演算子を使用して文字列を連結することができます。
let str1 = "Hello" let str2 = "world" let str3 = str1 + " " + str2 print(str3) // "Hello world"
また、+=演算子を使用して文字列を更新することもできます。
var str4 = "Hello" str4 += " world" print(str4) // "Hello world" var str4 = "Hello" str4 += " world" print(str4) // "Hello world"
文字列に値を埋め込む方法
[編集]以下は、Swiftで文字列に値を埋め込む方法の例です。\(value)のように、文字列内に変数を\( )で囲んで埋め込みます。
var name = "John" var age = 25 var message = "\(name) is \(age) years old." print(message)
- 実行結果
John is 25 years old.
また、String(format: "%@", value)のように、Stringクラスのformatメソッドを使用することもできます。以下は例です。
var name = "John" var age = 25 var message = String(format: "%@ is %d years old.", name, age) print(message)
- 実行結果
John is 25 years old.
どちらの方法でも同じ結果が得られます。
サロゲートペア
[編集]このSwiftのコードは、文字列をUTF-8形式およびUTF-16形式でそれぞれカウントする方法と、文字列長を計算する方法を示しています。
import Foundation // Foundationフレームワークをインポート let str = "aαあ𪚲" // 文字列を定義(ASCII, ギリシャ文字, ひらがな, 片仮名, および漢字等を含む) print(str.utf8.count) // UTF-8形式での文字列の長さをカウント(バイト単位):10 print(str.utf16.count) // UTF-16形式での文字列の長さをカウント(16ビット単位):5 print((str as NSString).length) // UTF-16形式での文字列の長さを計算:5 print(str._bridgeToObjectiveC().length) // 上と同様にUTF-16形式での文字列の長さを計算:5 let char = str.character(at:3) // 4番目の文字のUnicode scalarを見つける print(type(of: char)) // UInt16(16ビット符号なし整数型) print(String(format:"%x", char)) // d869(4番目の文字のUnicode scalarを16進数表記で出力)
このコードでは、Foundation
フレームワークがインポートされているため、NSString
のメソッドであるlength
メソッドにアクセスできます。
また、character(at:)
はNSString
にも実装されていますが、Swift
の組み込みのString
型でも提供されています。
配列
[編集]Swiftにおける配列は、複数の値を一箇所に保持することができるデータ構造です。Swiftでは、配列を宣言するために2つの方法があります。以下の表では、それぞれの方法について解説します。
配列の宣言方法 説明 Array<要素の型>()
空の配列を定義する方法です。 [要素の型]()
空の配列を定義する省略形です。
以下は、Swiftにおける配列のバリエーションとリテラルのコード例です。
空の配列を定義する場合
var emptyArray: Array<Int> = Array<Int>() var emptyArray2: [Int] = [Int]() var emptyArray3: [Int] = []
ここで、Array<Int>()
と[Int]()
は、空の配列を宣言するための方法で、それぞれemptyArray
とemptyArray2
に割り当てられます。同様に、[]
は空の配列を宣言する省略形で、emptyArray3
に割り当てられます。
配列のリテラルを使用する場合
var arrayWithLiteral1: [Int] = [1, 2, 3] var arrayWithLiteral2 = [4, 5, 6]
ここで、[1, 2, 3]
と[4, 5, 6]
は、配列のリテラルと呼ばれるもので、それぞれarrayWithLiteral1
とarrayWithLiteral2
に割り当てられます。
配列にアクセスする方法
var fruits = ["apple", "banana", "orange"] print(fruits[0]) // "apple" print(fruits[1]) // "banana" print(fruits[2]) // "orange"
配列には、各要素に対応するインデックスがあります。上の例では、fruits
の最初の要素にアクセスするには、fruits[0]
と書きます。インデックスは0から始まり、配列の最後の要素にアクセスするインデックスは(配列の要素数 - 1)
となります。
配列の結合
[編集]+
演算子を使った配列の結合
[編集]+
演算子を使うと、2つの配列を結合することができます。例えば、以下のように書くことができます。
let array1 = [1, 2, 3] let array2 = [4, 5, 6] let joinedArray = array1 + array2 print(joinedArray) // => [1, 2, 3, 4, 5, 6]
+=
演算子を使った配列の結合
[編集]+=
演算子を使うと、配列自身を変更して結合することができます。例えば、以下のように書くことができます。
var array3 = [7, 8, 9] array3 += [10, 11, 12] print(array3) // => [7, 8, 9, 10, 11, 12]
配列の代入
[編集]Swiftの配列は値型であるため、代入する場合にはコピーが作成されます。つまり、元の配列に影響を与えずに別の配列に代入することができます。例えば、以下のように書くことができます。
var array4 = [13, 14, 15] var array5 = array4 array5.append(16) print(array4) // => [13, 14, 15] print(array5) // => [13, 14, 15, 16]
ただし、配列内の要素が参照型である場合は、代入先の配列でも同じオブジェクトが参照されるため、元の配列の要素に変更を加えた場合には代入先の配列にも同じ変更が反映されます。
基本操作
[編集]var arr = [0, 1, 2, 3, 4, 5, 6] arr.append(7) print(arr) // [0, 1, 2, 3, 4, 5, 6, 7] arr += [8, 9, 10] print(arr) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] arr.insert(11, at:2) print(arr) // [0, 1, 11, 2, 3, 4, 5, 6, 7, 8, 9, 10] arr[2] = 12 print(arr) // [0, 1, 12, 2, 3, 4, 5, 6, 7, 8, 9, 10] arr[2...2] = [11, 12, 13] print(arr) // [0, 1, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9, 10] arr.remove(at:0) print(arr) // [1, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9, 10] arr.removeLast() print(arr) // [1, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9] arr[5...] = [99] print(arr) // [1, 11, 12, 13, 2, 99] let index13 = arr.index(of: 13) print(index13) // Optional(3) let squared = arr.map{ i in i * i } print(squared) // [1, 121, 144, 169, 4, 9801] let filtered = arr.filter{ n in n > 3 } print(filtered) // [11, 12, 13, 99] let sorted = arr.sorted{ (a, b) in a > b } print(sorted) // [99, 13, 12, 11, 2, 1] let sum = arr.reduce(0){ (s, n) in s + n } print(sum) // 138 arr.forEach{ n in print(n) } // 1↵121↵144↵169↵4↵9801 let arr1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] let arr1_1 = arr1.map{ a -> [Int] in a.reversed() } print(arr1_1) // [[3, 2, 1], [6, 5, 4], [9, 8, 7]] let arr1_2 = arr1.flatMap{ a -> [Int] in a.reversed() } print(arr1_2) // [3, 2, 1, 6, 5, 4, 9, 8, 7] let arr2 = [1, 2, nil, 4] let arr2_1 = arr2.map{ n -> Int? in n } print(arr2_1) // [Optional(1), Optional(2), nil, Optional(4)] let arr2_2 = arr2.flatMap{ n -> Int? in n } print(arr2_2) // [1, 2, 4]
このコードは、Swift言語で配列に対して行うことができるいくつかの操作を示しています。
最初に、配列の要素を追加する方法が示されています。append()
メソッドは、配列の末尾に要素を追加します。+=
演算子は、別の配列を末尾に追加することもできます。
次に、配列内の要素を挿入したり、置き換えたり、削除したりする方法が示されています。insert()
メソッドは、特定の位置に要素を挿入します。要素を置き換えるには、要素のインデックスを指定して値を割り当てます。remove()
メソッドは、指定したインデックスの要素を削除します。removeLast()
メソッドは、配列の末尾の要素を削除します。また、配列スライスを使用して、複数の要素を一度に置き換えることもできます。
次に、配列を操作して新しい配列を作成する方法が示されています。map()
メソッドは、配列内のすべての要素に対して関数を適用し、新しい配列を返します。filter()
メソッドは、配列内の要素をフィルタリングし、指定した条件を満たす要素だけを含む新しい配列を返します。sorted()
メソッドは、配列内の要素をソートし、新しい配列を返します。reduce()
メソッドは、配列内の要素を畳み込んで単一の値を返します。forEach()
メソッドは、配列内のすべての要素に対して、指定されたクロージャを実行します。
最後に、多次元配列に対して操作を行う方法が示されています。map()
メソッドは、多次元配列内のすべての配列に対して、それぞれの要素の順序を反転した新しい配列を返します。flatMap()
メソッドは、多次元配列内のすべての要素を1つの配列に平坦化します。また、オプショナル型の値を含む配列に対しても動作します。map()
メソッドはオプショナル型の値を含む配列に対しては動作しますが、flatMap()
メソッドはnil
値を削除して新しい配列を返します。
集合
[編集]Swiftの集合(Set)は、ユニークで順序がない要素のコレクションです。それぞれの要素は同じ型でなければなりません。重複した要素は1つの要素として扱われます。
以下は、Swiftの集合に関する基本的な機能の例です。
// 空の集合を作成する var setA = Set<Int>() print(setA) // [] // 初期値を指定して集合を作成する var setB: Set<Int> = [1, 2, 3] print(setB) // [1, 2, 3] // 集合に要素を追加する setA.insert(1) setA.insert(2) setA.insert(3) setA.insert(4) print(setA) // [1, 2, 3, 4] // 集合から要素を削除する setA.remove(3) print(setA) // [1, 2, 4] // 集合の要素数を取得する print(setA.count) // 3 // 集合に要素が含まれているかを調べる print(setA.contains(1)) // true print(setA.contains(3)) // false // 集合を空にする setA.removeAll() print(setA) // [] // 集合の演算 let set1: Set<Int> = [1, 2, 3, 4] let set2: Set<Int> = [3, 4, 5, 6] // 和集合を取得する let unionSet = set1.union(set2) print(unionSet) // [1, 2, 3, 4, 5, 6] // 積集合を取得する let intersectionSet = set1.intersection(set2) print(intersectionSet) // [3, 4] // 差集合を取得する let subtractingSet = set1.subtracting(set2) print(subtractingSet) // [1, 2] // 対称差集合を取得する let symmetricDifferenceSet = set1.symmetricDifference(set2) print(symmetricDifferenceSet) // [1, 2, 5, 6]
Swiftの集合(Set)には、forループや高階関数を使用して要素を処理することができます。
まず、forループを使用する場合は、次のように記述します。
let set: Set = ["apple", "orange", "banana"] for element in set { print(element) } // apple // orange // banana
この例では、Set
の要素をfor-in
ループで1つずつ取り出し、それぞれの要素を処理しています。
次に、高階関数を使用する場合は、map()
、filter()
、reduce()
のような関数が使用できます。
例えば、map()
関数を使用して、Set
の各要素に対して操作を行い、新しいSet
を作成することができます。
let set: Set = [1, 2, 3, 4, 5] let mappedSet = set.map { $0 * 2 } print(mappedSet) // [2, 4, 10, 8, 6]
この例では、Set
の各要素を2倍して、新しいSet
を作成しています。map()
関数は、クロージャーを引数として受け取り、そのクロージャーでSet
の各要素を操作しています。
また、filter()
関数を使用して、Set
の要素をフィルタリングすることができます。
let set: Set = [1, 2, 3, 4, 5] let filteredSet = set.filter { $0 % 2 == 0 } print(filteredSet) // [4, 2]
この例では、Set
の要素を2で割った余りが0のものだけを取り出し、新しいSet
を作成しています。
最後に、reduce()
関数を使用して、Set
の要素を畳み込むことができます。
let set: Set = [1, 2, 3, 4, 5] let sum = set.reduce(0) { $0 + $1 } print(sum) // 15
この例では、Set
の各要素を合計しています。reduce()
関数は、初期値とクロージャーを引数として受け取り、Set
の各要素を畳み込んで、1つの値を生成しています。
辞書
[編集]Swiftの辞書(Dictionary)は、キーと値のペアを保存するためのコレクションです。キーと値はそれぞれ異なるデータ型である必要があります。辞書は、値を取得するためのキーを使用してアクセスできます。
辞書は、以下のように宣言されます。
var dict: [KeyType: ValueType] = [KeyType: ValueType]()
ここで、KeyTypeはキーのデータ型を表し、ValueTypeは値のデータ型を表します。例えば、文字列をキーに持ち整数を値に持つ辞書は以下のように宣言できます。
var numberDict: [String: Int] = ["one": 1, "two": 2, "three": 3]
辞書の要素にアクセスするには、キーを使用します。
print(numberDict["one"]) // Optional(1) print(numberDict["four"]) // nil
存在しないキーにアクセスすると、nilが返されます。辞書にキーが存在するかどうかを確認するには、contains
メソッドを使用します。
if numberDict.contains(where: { $0.key == "one" }) { print("Key exists") } else { print("Key does not exist") }
辞書に要素を追加するには、新しいキーと値のペアを辞書に追加します。
numberDict["four"] = 4
辞書から要素を削除するには、キーを指定してremoveValue(forKey:)
メソッドを使用します。
numberDict.removeValue(forKey: "four")
辞書の要素を変更するには、新しい値をキーに割り当てます。
numberDict["one"] = 10
辞書のキーと値を反復処理するには、for-in
ループを使用します。
for (key, value) in numberDict { print("\(key): \(value)") }
辞書のキーを反復処理するには、keys
プロパティを使用します。
for key in numberDict.keys { print(key) }
辞書の値を反復処理するには、values
プロパティを使用します。
for value in numberDict.values { print(value) }
辞書にも、配列や集合と同様に map
、filter
、reduce
などの高階関数が使用できます。
map
は辞書の各要素に対して、変換処理を適用し、新しい辞書を作成するメソッドです。変換処理は、クロージャーの引数として、各要素の (key, value)
のタプルを取ります。以下は、辞書 dict
の各要素の値を2倍にした新しい辞書を作成する例です。
let dict = ["a": 1, "b": 2, "c": 3] let doubled = dict.map { (key, value) in (key, value * 2) } print(doubled) // ["a": 2, "b": 4, "c": 6]
filter
は辞書の各要素に対して、指定した条件を満たす要素だけを抽出して、新しい辞書を作成するメソッドです。条件は、クロージャーの引数として、各要素の (key, value)
のタプルを取り、条件式を返します。以下は、値が奇数の要素だけを抽出した新しい辞書を作成する例です。
let dict = ["a": 1, "b": 2, "c": 3] let oddValues = dict.filter { (key, value) in value % 2 != 0 } print(oddValues) // ["a": 1, "c": 3]
reduce
は辞書の各要素を畳み込んで、1つの値を生成するメソッドです。初期値と、畳み込み処理をクロージャーの引数として渡します。畳み込み処理は、初期値と各要素の (key, value)
のタプルを引数として受け取り、新しい値を返します。以下は、辞書 dict
の全要素の値の和を求める例です。
let dict = ["a": 1, "b": 2, "c": 3] let sum = dict.reduce(0) { (result, keyValue) in let (_, value) = keyValue return result + value } print(sum) // 6
タプル
[編集]Swiftのタプルは、複数の異なるデータ型を一つのグループにまとめることができます。タプルは、複数の値を返すときにも便利です。
以下は、タプルの例です。
let myTuple = ("apple", 3.14, true) print(myTuple) // 出力: ("apple", 3.14, true)
タプルには、複数の要素をカンマ区切りで指定し、丸括弧で囲みます。
タプルの各要素は、インデックスを指定してアクセスすることができます。
let myTuple = ("apple", 3.14, true) print(myTuple.0) // 出力: apple print(myTuple.1) // 出力: 3.14 print(myTuple.2) // 出力: true
また、タプルの各要素に名前を付けることもできます。
let myTuple = (name: "apple", price: 100, isOnSale: true) print(myTuple.name) // 出力: apple print(myTuple.price) // 出力: 100 print(myTuple.isOnSale) // 出力: true
タプルは、関数の戻り値としても利用できます。複数の値を返す場合、タプルを使うとコードを簡潔に記述することができます。
func findMinMax(array: [Int]) -> (min: Int, max: Int) { var min = array[0] var max = array[0] for value in array { if value < min { min = value } if value > max { max = value } } return (min, max) } let result = findMinMax(array: [3, 5, 2, 8, 1]) print("最小値: \(result.min), 最大値: \(result.max)") // 出力: 最小値: 1, 最大値: 8
この例では、findMinMax
関数がタプルを返し、そのタプルには、最小値と最大値の値が含まれます。戻り値を受け取ったresult
は、タプルであるため、.min
と.max
を使って値を取得することができます。
オプショナル型
[編集]Swiftにおけるオプショナル(Optional)は、値が存在する場合にはその値を持ち、存在しない場合にはnil
という値を持つ特殊な型です。つまり、オプショナルは存在しない値を表現するための型です。
例えば、Int
型の変数x
を宣言したとき、その初期値はnil
となります。
var x: Int?
このようにオプショナルを使うことで、変数に値が入るまで型を決めずに宣言することができます。
オプショナルの値にアクセスするには、アンラップ(unwrapping)が必要です。アンラップとは、オプショナルの値がnil
でないことを確認してから、値にアクセスすることです。
以下は、オプショナルの値にアクセスする方法の例です。
var optionalInt: Int? = 10 // optionalIntがnilでないことを確認してからアクセスする if let unwrappedInt = optionalInt { print(unwrappedInt) } // 強制的にアンラップする let unwrappedInt2 = optionalInt! print(unwrappedInt2)
上記の例では、オプショナル型の変数optionalInt
に値10
を代入しています。その後、if let
文でoptionalInt
がnil
でないことを確認し、unwrappedInt
にアンラップした値を代入しています。また、!
演算子を使って強制的にアンラップすることもできます。ただし、optionalInt
がnil
である場合にはランタイムエラーが発生します。
また、オプショナル型には、guard let
文を使ってもアンラップすることができます。
func foo(optionalStr: String?) { guard let unwrappedStr = optionalStr else { print("optionalStr is nil") return } print(unwrappedStr) } foo(optionalStr: "Hello, world!") // Hello, world! foo(optionalStr: nil) // optionalStr is nil
この例では、foo
関数の引数optionalStr
がnil
でない場合には、unwrappedStr
にアンラップされた値を使って処理を行い、nil
である場合にはguard
文の中の処理を行っています。
オプショナル型には、Null合体演算子( Nil-Coalescing Operator )??
を使用して、オプショナルの値がnilである場合に、デフォルト値を返すこともできます。
var optionalStr: String? = nil let unwrappedStr = optionalStr ?? ""
- オプショナル連鎖
Swiftにおいて、オプショナルの値に対して簡潔かつ安全にアクセスする方法の1つに、「オプショナル連鎖 (Optional chaining)」があります。
オプショナル連鎖は、オプショナルのプロパティにアクセスする際に使用する「?」を使って、プロパティの存在を確認しながらアクセスを行うことができます。オプショナル連鎖が適用された式の値は、オプショナル値として評価されます。
以下は、オプショナル連鎖の例です。
struct Person { var name: String var age: Int? } let person: Person? = Person(name: "John", age: 30) // オプショナル連鎖を使用せずにageにアクセスすると、コンパイルエラーになる // let age = person.age // オプショナル連鎖を使用してageにアクセスする let age = person?.age print(age) // Optional(30)
上記の例では、person
はPerson
型のオプショナル値であり、そのage
プロパティもオプショナルです。let age = person?.age
とすることで、person
がnil
でない場合に限り、age
にアクセスされます。age
の型は、Int?
となります。
また、オプショナル連鎖は、プロパティだけでなくメソッドやサブスクリプトにも適用することができます。以下は、オプショナル連鎖を使用したメソッドの例です。
struct Car { var name: String func drive() { print("\(name) is driving.") } } var car: Car? = Car(name: "Tesla") // オプショナル連鎖を使用してdriveメソッドにアクセスする car?.drive() // "Tesla is driving." car = nil // オプショナル連鎖を使用してdriveメソッドにアクセスする car?.drive() // 何も出力されない
上記の例では、car
はCar
型のオプショナル値です。car?.drive()
とすることで、car
がnil
でない場合に限り、drive
メソッドが実行されます。
オプショナル連鎖の利用により、アンラップ処理を簡潔かつ安全に行うことができます。 ただし、オプショナル値を使用する場合は、値が存在しない場合に備えたハンドリングを行うことが重要です。
演算子
[編集]Swiftにおいて、式はプログラムで何かしらの値を生成するための構成要素です。式は、変数やリテラル、関数呼び出し、演算子、そしてそれらを組み合わせたものから構成されます。
演算子は、式内で値を操作するために使用されます。 Swiftには、算術演算子、比較演算子、論理演算子、代入演算子、範囲演算子、ビット演算子など、さまざまな種類の演算子があります。演算子は、変数や定数、リテラル値などのオペランドに対して、特定の操作を実行するために使用されます。
演算子は、一般的に、単項演算子(1つのオペランドを取る)、二項演算子(2つのオペランドを取る)または三項演算子(3つのオペランドを取る)に分類されます。Swiftでは、いくつかの演算子は、オプショナルや範囲、タプルなどの機能に特化しています。
このセクションでは、Swiftで使用できる演算子について詳しく説明します。演算子の優先順位、結合性、および使用例を示し、適切な演算子を使用して式を組み立てる方法を紹介します。
算術演算子
[編集]文法 | 意味 |
---|---|
+ |
加算 |
- |
減算 |
* |
乗算 |
/ |
除算(整数型のゼロ除算はエラーとなります) |
% |
剰余(整数型のゼロでの剰余演算はエラーとなります) |
&+ |
加算(オーバーフローを無視します) |
&- |
減算(オーバーフローを無視します) |
&* |
乗算(オーバーフローを無視します) |
&/ |
除算(オーバーフローを無視し、ゼロ除算の結果は0 となります)(※廃止済み)
|
&% |
剰余(オーバーフローを無視し、ゼロでの剰余演算の結果は0 となります)(※廃止済み)
|
文法 | 意味 |
---|---|
+ |
単項プラス |
- |
単項マイナス(符号反転) |
++ |
インクリメント(式はインクリメントされた後の値を返す)※Swift 3.0で廃止[1] |
-- |
デクリメント(式はデクリメントされた後の値を返す)※Swift 3.0で廃止[1] |
文法 | 意味 |
---|---|
++ |
インクリメント(式はインクリメントされる前の値を返す)※Swift 3.0で廃止[1] |
-- |
デクリメント(式はデクリメントされる前の値を返す)※Swift 3.0で廃止[1] |
比較演算子
[編集]
|
|
|
print(1...5 ~= 3) // true
var str = "mission control" print(str.range(of: "control")! ~= str.index(of: "c")!) // true
論理演算子
[編集]文法 | 意味 |
---|---|
&& |
論理AND |
|| |
論理OR |
! |
論理NOT |
三項演算子
[編集]文法 | 意味 |
---|---|
条件 ? 式1 : 式2 |
条件が真のとき式1の値を、偽のとき式2の値を返す |
nil結合演算子
[編集]文法 | 意味 |
---|---|
?? |
左オペランドにはT? 型、右オペランドにはT 型の値をとり、左オペランドに値が存在していればアンラップしてその値を返し、左オペランドが nil であれば右オペランドの値を返す
|
"x".toInt() ?? 0 // 0 "5".toInt() ?? 0 // 5
ビット演算子
[編集]文法 | 意味 |
---|---|
<< |
左シフト |
>> |
右シフト(左オペランドが符号付き整数の場合は算術シフト、符号無し整数の場合は論理シフト) |
& |
AND |
| |
OR |
^ |
XOR |
~ |
NOT(ビット反転) |
代入演算子
[編集]
|
|
範囲演算子
[編集]文法 | 意味 |
---|---|
..< |
半分開いた範囲(終端を含まない):半開区間 |
... |
閉じた範囲(終端を含む):閉区間 |
キャスト演算子
[編集]文法 | 意味 |
---|---|
is |
型検査 |
as |
型キャスト |
as? |
オプショナル型へのキャスト キャストできない場合はnilとなる |
as! |
強制型キャスト キャストできない場合は実行時エラーとなる |
- キャスト演算子
var arr:[Any] = [1, 2.0, "3", -4] for item in arr { let intItem = item as? Int print(intItem) }
- 実行結果
Optional(1) nil nil Optional(-4)
import Foundation var array1 = [1, 2, 3, 4] as NSArray var mutableArray1 = array1 as? NSMutableArray // ダウンキャストできないので、nilになる // ※補足: 上記の例の場合、Mutableな状態で取得したければ array1.mutableCopy() を行うべき。 var mutableArray2 = [1, 2, 3, 4] as NSMutableArray var array2 = mutableArray2 as NSArray var mutableArray2_2 = array2 as? NSMutableArray // 元々mutableArray2の型はNSMutableArrayなので、キャストに成功する
その他
[編集]識別子にはUnicode文字を用いることができます。
let リンゴの数 = 3 let みかんの数 = 5
文字列リテラルの中にある\(...)
には、式の結果が展開される
let リンゴ説明 = "私は\(リンゴの数)個のリンゴを持っています。" // ”私は3個のリンゴを持っています。" let 果物説明 = "私は\(リンゴの数 + みかんの数)個の果物を持っています。" //"私は8個の果物を持っています。"
ヒアドキュメントには、ダブルクォーテーション3つを使用します。ヒアドキュメント内の行頭の空白は自動的にトリミングされます。
let tanka = """ 田子の浦に うち出でてみれば 白妙の 富士の高嶺に 雪は降りつつ """ print(tanka)
数値リテラルのプレフィックスは"0b
"で2進数、"0o
"で8進数、"0x
"で16進数を表す。
let dec = 29 let bin = 0b11101 // 2進数で29 let oct = 0o35 // 8進数で29 let hex = 0x1D // 16進数で29
浮動小数点リテラルは、通常の十進数表記に加え16進数表記もサポートしています。
let π = 3.1415926535897931 let pi = 0x1.921fb54442d18p+1 // NSString(format:"%a", π) の出力と同様
整数型と浮動小数点型のどちらでも、コードの見やすさのためにアンダースコア _ を桁の区切りとして挿入できます。
let threeHundledMillion = 300_000_000 let bitMask: UInt8 = 0b0010_0000
アンダースコアは、代入文で代入する値を無視したいときに、仮の代入先として使用できます。
var s:String? = "String" if let _ = s { print("sはnilではありません。") }
for _ in 0..<5 { print("repeat") }
let result = (404, "Not Found", false) let (_, message, _) = result
他言語との比較
[編集]C言語との類似点
[編集]- ほとんどのC言語の演算子はSwiftでも使用できます。
- ただし、Swiftではオーバーフローを伴う数値演算のサポートのための演算子が追加されています。
- 中括弧は、文をグループ化するために使用されます。
- 等号1つ
=
は代入、2つ==
は等価比較を意味します。- この他に、Swiftでは等号3つ
===
は同じオブジェクトを参照しているかどうかを確認するための演算子を意味します。
- この他に、Swiftでは等号3つ
while
、if
、for
等の制御文が類似しています。- ただし、Swiftでは拡張機能を有します。例えば、
while
、if
文はパターンマッチングや条件付きOptionalアンラップをサポートします。
- ただし、Swiftでは拡張機能を有します。例えば、
- 角括弧は、配列の宣言と配列の要素取得の両方で使用されます。
Objective-Cとの類似点
[編集]- 基本的な数値型
Int
、UInt
、Float
、Double
等のサポート。 - クラスメソッドは、インスタンスメソッドと同様に継承されます。クラスメソッド内の
self
は、メソッドが呼び出されたクラスを意味します。 for
...in
列挙構文のサポート。
Objective-Cとの相違点
[編集]- 文はセミコロン(
;
)で終わる必要はありません。しかし、1行に複数の文を記述する際に使用することができます。 - ヘッダーファイルが存在しません。
/*
~*/
によるコメントはネストできます。- 型推論のサポート。
- ジェネリックプログラミングのサポート。
- 関数は第一級オブジェクトです。
- 演算子はクラスに対して再定義(演算子のオーバーロード)でき、新しい演算子を定義できます。
- 文字列はUnicodeを完全にサポートします。ほとんどのUnicode文字は識別子や演算子でも使用できます。
- 例外処理は存在しません。Swift 2では例外処理とは互換性のない別のエラー処理モデルが導入されています。
- バグの原因となるC言語ファミリーの特徴がいくつか削除されています。
- デフォルトでは、ポインタは公開されていません。プログラマが参照の管理をする必要はありません。
- 変数割り当ては値を返しません。これにより、
=
と==
の誤用を防ぐことができます。 switch
文内でbreak
を行う必要はありません。明示的にfallthrough
を行わない限り次のcase
にフォールスルーすることはありません。- 変数と定数は常に初期化され、配列の境界は常にチェックされます。
- 算術オーバーフローは実行時エラーとしてトラップされます。オーバーフローを許可する演算子は
&+
、&-
、&*
、&/
、&%
として定義されます。また、全ての整数型にはプロパティmin
、max
が定義されており、潜在的なオーバーフローのチェックに利用することができます。 - ブロックを用いない1行の
if
文、while
文はサポートされていません。 - Off-by-oneエラーの原因となるC言語スタイルの
for (int i = 0; i < c; i++)
文は、Swift 3で削除されました。 - インクリメント演算子
++
、デクリメント演算子--
は、Swift 3で削除されました。
脚註
[編集]外部リンク
[編集]附録
[編集]コードギャラリー
[編集]エラトステネスの篩
[編集]エラトステネスの篩を、若干 Swift らしく書いてみました。
- エラトステネスの篩
import Foundation func sieveOfEratosthenes(_ n: Int) -> [Int] { var sieve = Array(repeating: true, count: n + 1) sieve[0] = false sieve[1] = false let sqrtN = Int(sqrt(Double(n))) for i in 2...sqrtN { if sieve[i] { for j in stride(from: i * i, through: n, by: i) { sieve[j] = false } } } return sieve.enumerated().compactMap { $1 ? $0 : nil } } print(sieveOfEratosthenes(100))
- 実行結果
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
最大公約数と最小公倍数
[編集]最大公約数と最小公倍数を、若干 Swift らしく書いてみました。
- 最大公約数と最小公倍数
func gcd2(_ m: Int, _ n: Int) -> Int { n == 0 ? m : gcd2(n, m % n) } func gcd(_ args: Int...) -> Int { guard let first = args.first else { return 0 } return args.suffix(args.count - 1).reduce(first){ gcd2($0, $1) } } func lcm2(_ m: Int, _ n: Int) -> Int { m * n / gcd2(m, n) } func lcm(_ args: Int...) -> Int { guard let first = args.first else { return 0 } return args.suffix(args.count - 1).reduce(first){ lcm2($0, $1) } } print("gcd2(30, 45) => \(gcd2(30, 45))") print("gcd(30, 72, 12) => \(gcd(30, 72, 12))") print("lcm2(30, 72) => \(lcm2(30, 72))") print("lcm(30, 42, 72) => \(lcm(30, 42, 72))")
- 実行結果
gcd2(30, 45) => 15 gcd(30, 72, 12) => 6 lcm2(30, 72) => 360 lcm(30, 42, 72) => 2520
二分法
[編集]二分法を、若干 Swift らしく書いてみました。
- 二分法
func bisection(low: Double, high: Double, f: (Double) -> Double) -> Double { var low = low var high = high var x = (low + high) / 2.0 var fx = f(x) if abs(fx) < 1.0e-10 { return x } if fx < 0.0 { low = x } else { high = x } return bisection(low: low, high: high, f: f) } let result1 = bisection(low: 0, high: 3) { x in return x - 1 } print(result1) let result2 = bisection(low: 0, high: 3) { x in return x * x - 1 } print(result2)
- 実行結果
0.9999999999417923 1.0000000000291038
- 旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法の例を Swift に移植しました。
外部リンク
[編集]- [公式サイト]