コンテンツにスキップ

Swift

出典: フリー教科書『ウィキブックス(Wikibooks)』
Wikipedia
Wikipedia
ウィキペディアSwift (プログラミング言語)の記事があります。

Swiftは、Appleが、iOS、iPadOS、macOS、tvOS、および watchOS のために開発したオープンソースのプログラミング言語です。

目次

[編集]

環境準備

[編集]

オンラインコンパイル実行

[編集]

ローカルにコンパイル実行環境を作るのが困難、すぐにコンパイルと実行を試してみたい。そんな場合には、オンラインコンパイル実行環境を使うことも検討に値します。

Swift Playgrounds
公式のオンラインコンパイル実行環境です。
paiza.io
複数のプログラミン言語に対応しており、Swiftにも対応しています。

ビルド済みバイナリーの導入

[編集]

公式サイトの、Download Swiftからインストーラーを入手するのが基本です。

iPadOS
Appleから、「Swift Playgrounds」が学生を対象に公開されています。
Swift Playgroundsは開発環境であるとともに、Swiftの基本を習得するためのチュートリアルを含んでいます。
macOS
Swiftを開発したAppleのプラットフォームです。
macOS 10.9 以降をサポートしています。
Xcodeに統合されています。
複数のツールチェインがSwiftをサポートしているので、目的に応じて切り替えて使用します。
Windows
Windows10以降をサポートしています(Windows8.1以前はサポート対象外)。swift公式サイトではwingetなどパッケージマネージャを用いてインストールすることを推奨している[1](※2023年現在、公式サイトのwinget関連のコマンドを使っても、うまく行かなかった)。なおそのダウンロード内容は、swift公式サイトのSDKのインストールに加えて、さらに Visual Studio 2019 以降をインストールして『C++によるデスクトップ開発』の標準設定をインストールする必要がある。なお、Powershellからでもコマンドプロンプトからでも、どちらかでもコマンド実行できる。
GNU/Linuxのディストリビューション
GNU/Linuxのディストリビューションの場合、公式にサポートされているディストリビューションと公式にはサポートされず独自にパッケージを用意していディストリビューションがあります。

公式にサポートされているディストリビューション

[編集]

公式サイトの、Download SwiftからTARBALLをダウンロードしてインストールします。

Ubuntu
18.04 以降をサポートしています。
CentOS
7 以降をサポートしています。
Amazon Linux
2 以降をサポートしています。

公式にサポートされていないディストリビューション

[編集]

パッケージマネージャによってパッケージ名と依存関係、インストールに使用するコマンドが異なります。

公式では、RPMでも提供されていますが experimental use only との位置づけです

Fedoraの場合
Fedora 独自のパッケージリポジトリーから非公式版をインストールします。
最新の利用可能なバージョンへインストール済みパッケージを同期する
$ sudo dnf distro-sync
インストール
$ sudo dnf install swift-lang

ソースコードからのビルド

[編集]

最新のあるいはリリース前のバージョンのSwiftをインストールしたい場合は、ソースコードを入手しビルドします。

  1. ソースコードの入手
    https://github.com/apple/swift から clone します。
  2. ビルド環境の構築
    ビルド環境の構築のために build-toolchain スクリプトが用意されています。--help で全てのオプションの説明を見ることが出来ます。
  3. コンフィギュレーション
  4. ビルド
  5. テスト
  6. インストール

バージョン確認コマンド

[編集]

インストールに成功したと思ったら、バージョン確認など簡単なコマンドを実行してみましょう。

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" に型アノテーションを書くべきだと主張する人はまれなので、定数と変数の名前えらびは重要で、単に ab の様な「色のない」 名前は選ぶべきではありません。

暗黙の型変換は行われない

[編集]

なお、いかなる数値型の変数についても暗黙の型変換( 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]()は、空の配列を宣言するための方法で、それぞれemptyArrayemptyArray2に割り当てられます。同様に、[]は空の配列を宣言する省略形で、emptyArray3に割り当てられます。

配列のリテラルを使用する場合

var arrayWithLiteral1: [Int] = [1, 2, 3]
var arrayWithLiteral2 = [4, 5, 6]

ここで、[1, 2, 3][4, 5, 6]は、配列のリテラルと呼ばれるもので、それぞれarrayWithLiteral1arrayWithLiteral2に割り当てられます。

配列にアクセスする方法

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)
}

辞書にも、配列や集合と同様に mapfilterreduce などの高階関数が使用できます。

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文でoptionalIntnilでないことを確認し、unwrappedIntにアンラップした値を代入しています。また、!演算子を使って強制的にアンラップすることもできます。ただし、optionalIntnilである場合にはランタイムエラーが発生します。

また、オプショナル型には、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関数の引数optionalStrnilでない場合には、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)

上記の例では、personPerson型のオプショナル値であり、そのageプロパティもオプショナルです。let age = person?.ageとすることで、personnilでない場合に限り、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() // 何も出力されない

上記の例では、carCar型のオプショナル値です。car?.drive()とすることで、carnilでない場合に限り、driveメソッドが実行されます。

オプショナル連鎖の利用により、アンラップ処理を簡潔かつ安全に行うことができます。 ただし、オプショナル値を使用する場合は、値が存在しない場合に備えたハンドリングを行うことが重要です。

演算子

[編集]

Swiftにおいて、式はプログラムで何かしらの値を生成するための構成要素です。式は、変数やリテラル、関数呼び出し、演算子、そしてそれらを組み合わせたものから構成されます。

演算子は、式内で値を操作するために使用されます。 Swiftには、算術演算子、比較演算子、論理演算子、代入演算子、範囲演算子、ビット演算子など、さまざまな種類の演算子があります。演算子は、変数や定数、リテラル値などのオペランドに対して、特定の操作を実行するために使用されます。

演算子は、一般的に、単項演算子(1つのオペランドを取る)、二項演算子(2つのオペランドを取る)または三項演算子(3つのオペランドを取る)に分類されます。Swiftでは、いくつかの演算子は、オプショナルや範囲、タプルなどの機能に特化しています。

このセクションでは、Swiftで使用できる演算子について詳しく説明します。演算子の優先順位、結合性、および使用例を示し、適切な演算子を使用して式を組み立てる方法を紹介します。

算術演算子

[編集]
二項演算子
文法 意味
+ 加算
- 減算
* 乗算
/ 除算(整数型のゼロ除算はエラーとなります)
% 剰余(整数型のゼロでの剰余演算はエラーとなります)
&+ 加算(オーバーフローを無視します)
&- 減算(オーバーフローを無視します)
&* 乗算(オーバーフローを無視します)
&/ 除算(オーバーフローを無視し、ゼロ除算の結果は0となります)(※廃止済み)
&% 剰余(オーバーフローを無視し、ゼロでの剰余演算の結果は0となります)(※廃止済み)
単項演算子(前置)
文法 意味
+ 単項プラス
- 単項マイナス(符号反転)
++ インクリメント(式はインクリメントされたの値を返す)※Swift 3.0で廃止[2]
-- デクリメント(式はデクリメントされたの値を返す)※Swift 3.0で廃止[2]
単項演算子(後置)
文法 意味
++ インクリメント(式はインクリメントされるの値を返す)※Swift 3.0で廃止[2]
-- デクリメント(式はデクリメントされるの値を返す)※Swift 3.0で廃止[2]

比較演算子

[編集]
比較演算
文法 意味
< より小さい
<= 以下
> より大きい
>= 以上
文法 意味
== 等しい
!= 等しくない
=== 同じオブジェクトへの参照
!== 別のオブジェクトへの参照
文法 意味
~= パターンマッチ(左辺の範囲内に右辺が有ればtrue)
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結合演算子

[編集]
nil結合演算子( Nil Coalescing Operator )
文法 意味
?? 左オペランドにはT?型、右オペランドにはT型の値をとり、
左オペランドに値が存在していればアンラップしてその値を返し、左オペランドがnilであれば右オペランドの値を返す
"x".toInt() ?? 0  // 0
"5".toInt() ?? 0  // 5

ビット演算子

[編集]
ビット演算子
文法 意味
<< 左シフト
>> 右シフト(左オペランドが符号付き整数の場合は算術シフト、符号無し整数の場合は論理シフト)
& AND
| OR
^ XOR
~ NOT(ビット反転)

代入演算子

[編集]
代入演算子
文法 意味
= 代入
+= 加算と代入
-= 減算と代入
*= 乗算と代入
%= 剰余演算と代入
/= 除算と代入
文法 意味
<<= 左ビットシフトと代入
>>= 右ビットシフトと代入
&= ビット演算ANDと代入
^= ビット演算XORと代入
|= ビット演算ORと代入
&&= 論理ANDと代入
||= 論理ORと代入

範囲演算子

[編集]
範囲演算子( Range operators )
文法 意味
..< 半分開いた範囲(終端を含まない):半開区間
... 閉じた範囲(終端を含む):閉区間

キャスト演算子

[編集]
キャスト演算子
文法 意味
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つ===は同じオブジェクトを参照しているかどうかを確認するための演算子を意味します。
  • whileiffor等の制御文が類似しています。
    • ただし、Swiftでは拡張機能を有します。例えば、whileif文はパターンマッチングや条件付きOptionalアンラップをサポートします。
  • 角括弧は、配列の宣言と配列の要素取得の両方で使用されます。

Objective-Cとの類似点

[編集]
  • 基本的な数値型IntUIntFloatDouble等のサポート。
  • クラスメソッドは、インスタンスメソッドと同様に継承されます。クラスメソッド内のselfは、メソッドが呼び出されたクラスを意味します。
  • for...in列挙構文のサポート。

Objective-Cとの相違点

[編集]
  • 文はセミコロン(;)で終わる必要はありません。しかし、1行に複数の文を記述する際に使用することができます。
  • ヘッダーファイルが存在しません。
  • /**/によるコメントはネストできます
  • 型推論のサポート。
  • ジェネリックプログラミングのサポート。
  • 関数は第一級オブジェクトです。
  • 演算子はクラスに対して再定義(演算子のオーバーロード)でき、新しい演算子を定義できます。
  • 文字列はUnicodeを完全にサポートします。ほとんどのUnicode文字は識別子や演算子でも使用できます。
  • 例外処理は存在しません。Swift 2では例外処理とは互換性のない別のエラー処理モデルが導入されています。
  • バグの原因となるC言語ファミリーの特徴がいくつか削除されています。
    • デフォルトでは、ポインタは公開されていません。プログラマが参照の管理をする必要はありません。
    • 変数割り当ては値を返しません。これにより、===の誤用を防ぐことができます。
    • switch文内でbreakを行う必要はありません。明示的にfallthroughを行わない限り次のcaseにフォールスルーすることはありません。
    • 変数と定数は常に初期化され、配列の境界は常にチェックされます。
    • 算術オーバーフローは実行時エラーとしてトラップされます。オーバーフローを許可する演算子は&+&-&*&/&%として定義されます。また、全ての整数型にはプロパティminmaxが定義されており、潜在的なオーバーフローのチェックに利用することができます。
    • ブロックを用いない1行のif文、while文はサポートされていません。
    • Off-by-oneエラーの原因となるC言語スタイルのfor (int i = 0; i < c; i++)文は、Swift 3で削除されました。
    • インクリメント演算子++、デクリメント演算子--は、Swift 3で削除されました。
SwiftにGCはありますか?
Swiftにはガベージコレクション(Garbage Collection, GC)はありません。代わりに、Swiftは自動参照カウント(Automatic Reference Counting, ARC)と呼ばれるメモリ管理手法を採用しています。ARCは、オブジェクトが参照されている限りメモリを保持し、参照がなくなった時点で自動的にメモリを解放する仕組みです。ARCは、Objective-Cでも採用されており、SwiftはObjective-Cランタイムを利用することができるため、Objective-Cで使用されるARCと互換性があります。

脚註

[編集]
  1. ^ [1]
  2. ^ 2.0 2.1 2.2 2.3 Accepted proposals for Swift 3.0

外部リンク

[編集]

附録

[編集]

コードギャラリー

[編集]

エラトステネスの篩

[編集]

エラトステネスの篩を、若干 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 に移植しました。