Swift

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

メインページ > 工学 > 情報技術 > プログラミング > Swift

Swiftとは、Appleが作成したオープンソースのプログラミング言語である。なお、iPadアプリ 「Swift Playgrounds」では、子供向けにSwiftの基本を解説している。

目次[編集]

定数と変数[編集]

定数はletキーワードで、変数はvarキーワードによって宣言する。

let 定数名:  = 
var 変数名:  = 

型推論が可能な場合には型の記述を省略できる。

let pi: Double = 3.141592
let pi = 3.141592  // 型名を省略する

なお、いかなる数値型の変数についても暗黙の型変換(cast)が行われることはなく、型の違う数値同士の演算、右辺と左辺で型の異なる代入は全てコンパイルエラーとなる。一方、数値リテラルについては、整数型から浮動小数点型への暗黙の型変換が行われることがある。

let intValue: Int = 1
let uintValue: UInt = intValue          // コンパイルエラー
let intValue2: Int = intValue + 1.0     // コンパイルエラー

基本データ型[編集]

数値[編集]

符号付き整数の型には、IntInt8Int16Int32Int64がある。Intは、32ビット環境ではInt32と同じサイズ、64ビット環境ではInt64と同じ。

符号無し整数の型には、UIntUInt8UInt16UInt32UInt64がある。UIntは、32ビット環境ではUInt32と同じサイズ、64ビット環境ではUInt64と同じ。

浮動小数点数の型には、IEEE 754 単精度Floatと、倍精度のDoubleがある。

先述の通り、上記の型に対して暗黙の型変換(cast)が行われることは一切ない。

文字列[編集]

Swiftの文字列はString型、文字はCharacter型である。UTF-16表現ではサロゲートペアを要する拡張領域の文字も1文字として扱う。

import Foundation

let str = "aαあ𪚲"
print(str.utf8.count) // 10
print(str.utf16.count) // 5
print((str as NSString).length) // 5 = length in UTF-16
print(str._bridgeToObjectiveC().length) // 5

let char = str.character(at:3)
print(type(of: char)) // UInt16
print(String(format:"%x", char)) // d869

配列[編集]

配列は、Array<型>ないし[型]と宣言する。より直感的な後者が推奨されている。

var arrayOfChars: [Character] // 宣言のみ
var arrayOfInts = [Int]() // 空の配列として初期化
let fibs = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] // [Int]
let transcendentals = [ 2.7182818284590451, 3.1415926535897931 ] // [Double]
let falsies = [false, 0, 0.0, ""] // [Any] 型の混在

なお、[]は空の配列リテラルであるが、要素の型を何らかの形で指定する必要がある

var ints: [Int] =        []
var doubles: [Double] =  []
var any: [Any] =         []
var unknown =            [] // 型推論ができないため、コンパイルエラー

配列の結合、代入

var ints1: ArraySlice<Int>        =      [10,20,30]
var ints2: ArraySlice<Int>        =      [40,50]
ints1 = ints1 + ints2       // [10,20,30,40,50]
ints2[1] = 55               // [40,55]
ints1[1...2] = ints2        // [10,40,55,40,50]
ints1 = ints2               // [40,55]

基本操作

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]

セット[編集]

重複のないコレクション型

var set = [3, 1, 4, 1, 5, 9, 2, 6] as Set
print(set)

基本操作

set.insert(5)
print(set) // [1, 3, 4, 9, 6, 5, 2]

set.remove(1)
print(set) // [3, 4, 9, 6, 5, 2]

print(set.contains(3)) // true

var arr = set.sorted()
print(arr) // [2, 3, 4, 5, 6, 9]

var digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] as Set
print(set.isSubset(of: digits)) // true

辞書[編集]

Swiftでは、連想配列は辞書(Dictionary)と呼ばれる。

辞書は総称型であり、キーの型KeyType、値の型ValueTypeを持つ辞書はDictionary<KeyType,ValueType>または[KeyType: ValueType]と宣言する。Array同様、より直感的な後者が推奨されている。

var name2num = [String:Int]()
name2num["zero"] = 0
name2num["one"]  = 1
let name2num = ["zero":0, "one":1, "two":2] // Dictionary<String, Int>

[:]が空の辞書リテラルであるが、キーの型および値の型が指定されている必要がある点は配列と同様である。

var dict: [String: Int] = [:]
var unknowndict = [:] // 型推論ができないため、コンパイルエラー

基本操作

var name2num = ["zero":0, "one":1, "two":2]

name2num["three"] = 3
print(name2num) // ["two": 2, "one": 1, "three": 3, "zero": 0]

name2num["zero"] = nil
print(name2num) // ["two": 2, "one": 1, "three": 3]

let one = name2num["one"]
print(one) // Optional(1)

print(name2num.keys) // ["two", "one", "three"]
print(name2num.values) // [2, 1, 3]

for element in name2num {
    print(element) // (key: "two", value: 2) ↵(key: "one", value: 1) ↵(key: "three", value: 3)
}

for (key, value) in name2num {
    print("\(key) = \(value)") // two = 2 ↵one = 1 ↵three = 3
}

name2num.forEach{ (key, value) in
    print("\(key) = \(value)") // two = 2 ↵one = 1 ↵three = 3
}

タプル[編集]

let errorStatus: (Int, String) = (404, "Not Found")
let errorStatus = (404, "Not Found")

タプルの各要素には名前をつけることができる。名前付きタプルに対して、数値インデックスによって各要素にアクセスすることも可能。

let okStatus = (code: 200, message: "OK")
print("\(okStatus.code): \(okStatus.message)")
print("\(okStatus.0): \(okStatus.1)")
// 200: OK
// 200: OK

代入式で定数や変数に分解することができる。この際にアンダースコアを指定すると、その値を無視することができる。

let (_, message) = okStatus

Optional型[編集]

Swiftでは安全性のため、ある型の定数や変数にnilを代入することは禁止されている。

変数にnilを代入可能にするには、Optional型でラップする。任意の型のOptional型は、ラップしたい型名の後ろに"?"を付けて表す。

var n: Int = nil  // コンパイルエラー
var m: Int? = nil // Optional型はnilを代入可能
var l: Optional<Int> = nil // Int? は Optional<Int> の略記である
var o:Int! = nil // 暗黙的開示Optional型。自動的に強制アンラップされるが、nilの場合に実行時エラーとなる
var p:ImplicitlyUnwrappedOptional<Int> = nil // Int! は ImplicitlyUnwrappedOptional<Int> の略記である
// 文字列の配列の中から指定した文字列を探す関数
func findString(data: [String], key: String) -> Int? {
    for index in 0..<data.count {
        if data[index] == key {
            return index
        }
    }
    return nil
}

Optional<Type>型の値からType型の値を取り出すことを、アンラップするという。

Optional型の値を強制的にアンラップするには、値のうしろに"!"を付ける。アンラップしようとした値がnilの場合は実行時エラーが発生する。

let fruits = ["apple", "banana", "orange"]
let possibleKiwiIndex = findString(data:fruits, key:"kiwi") // Int?型

if possibleKiwiIndex != nil {
    let kiwiIndex = possibleKiwiIndex!
    print("キウイは\(kiwiIndex)番目に見つかりました")
} else {
    print("キウイは見つかりませんでした")
}

if文とwhile文ではOptional束縛(optional binding)を記述できる。

if let kiwiIndex = find(fruits, "kiwi") {
    print("キウイは\(kiwiIndex)番目に見つかりました")
} else {
    print("キウイは見つかりませんでした")
}

Optional型の変数のメソッドやプロパティを呼び出す際には、Optional Chainingを記述できる。

var possibleOrangeIndex = findString(data:fruits, key:"orange")

if possibleOrangeIndex?.distance(to:1).signum() == -1 {
    print("オレンジは2番目より後にあります")
}
var path:String? = "/path/to/an/invisible/.file"
var isInvisible = path?.split(separator:"/").last?.hasPrefix(".")
print(isInvisible) // Optional(true)

演算子[編集]

算術演算子[編集]

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

比較演算子[編集]

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

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)[編集]

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

キャスト演算子[編集]

キャスト演算子
文法 意味
is 型検査
as 型キャスト
as? Optional型へのキャスト キャストできない場合は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で削除された。

脚注[編集]

  1. ^ 1.0 1.1 1.2 1.3 Accepted proposals for Swift 3.0

外部リンク[編集]