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     // コンパイルエラー

基本データ型[編集]

配列[編集]

配列は、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] 型の混在

辞書[編集]

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>

タプル[編集]

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)

制御構造[編集]

帰結節や繰り返す文は(ブレースで囲まれた)複文(ブロック)でなければならない。条件式を囲む括弧は不要である。

条件式に書けるのはBool型かBool型の式か、BooleanTypeプロトコルを実装する型でなければならない。

if文[編集]

 if 条件式1 {
     1
 } else if 条件式2 {
     2
 } else {
     3
 }

switch文[編集]

数値だけでなくあらゆるオブジェクトによって分岐することができる。また暗黙的にブレークされ、fallthroughキーワードがある場合に限り、次のcase文にフォールスルーする。

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

for/for-in文[編集]

let names = ["太郎", "花子", "一郎"]
for name in names {
    print("\(name)さん、こんにちは!")
}
 for index in 0..<5 {
     print("index: \(index)")
 }

while/repeat-while文[編集]

 while 条件式 {
     
 }
 repeat {
     
 } while 条件式

guard文[編集]

条件を満たさない場合の処理を記述するための構文。else句内では必ずスコープを抜ける処理(returnbreakthrow等)を記述する必要がある。

func intToUInt(_ n:Int) -> UInt {
    guard n >= 0 else {
        print("引数が0未満のため、正しく変換できませんでした。")
        return 0
    }
    return UInt(n)
}

let nums = [1, 0, -3]

for num in nums {
    let unum = intToUInt(num)
    print(unum)
}

// 実行結果:
// 1
// 0
// 引数が0未満のため、正しく変換できませんでした。
// 0

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

関数とクロージャ[編集]

関数とクロージャは第一級関数であり、普通のオブジェクトと同じように変数に入れたり、関数の引数や戻り値として受け渡しできる。

関数[編集]

func calcBMI(weight: Double, height: Double) -> Double {
    return weight / (height * height) * 10000
}
外部引数名[編集]

関数の引数には外部引数名(external parameter name)を指定することができる。対して関数内部のみで使用できる引数名はローカル引数名(local parameter names)と呼ぶ。

func calcBMI(weight w: Double, height h: Double) -> Double {
    return w / (h * h) * 10000
}

calcBMI(weight: 60, height: 160)  // 23.4375
可変長引数[編集]
func maxInt(first: Int, rest: Int...) -> Int {
    var max = first
    for num in rest {
        if max < num {
            max = num
        }
    }

    return max
}

maxInt(7, 3, 5, 9, 2)  // 9
maxInt(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

クロージャ[編集]

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 }

演算子[編集]

算術演算子[編集]

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

比較演算子[編集]

文法 意味
< より小さい
<= 以下
> より大きい
>= 以上
文法 意味
== 等しい
!= 等しくない
=== 同じオブジェクトへの参照
!== 別のオブジェクトへの参照
文法 意味
~= パターンマッチ
(左辺の範囲内に右辺が有ればtrue)

論理演算子[編集]

文法 意味
&& 論理AND
|| 論理OR
! 論理NOT

三項演算子[編集]

文法 意味
条件 ? 式1 : 式2 条件が真のとき式1の値を、偽のとき式2の値を返す

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? Optional型へのキャスト キャストできない場合はnilとなる

演算子オーバーロード[編集]

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 += (inout lhs: Vector2D, rhs: Vector2D) {
    lhs = lhs + rhs
}

prefix func ++ (inout rhs: 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)
let vec3 = vec1 + vec2  // {x 7.0, y 4.0}
var vec4 = -vec1        // {x -2.0, y -3.0}
vec4 += vec2            // {x 3.0, y -2.0}
++vec4                  // {x 4.0, y -1.0}

カスタム演算子[編集]

Swift標準の定義済み演算子(+*%など)の他に、全く新しい演算子を定義することができる。新しく定義したカスタム演算子は、標準の演算子と同じように演算子オーバーロードを実装できる。

// 中置演算子 ** を定義する
infix operator ** { precedence 155 associativity right }

func ** (lhs: Double, rhs: Double) -> Double {
    return pow(lhs, rhs)
}

2 ** 3 // 8.0

0.5 * 2 ** 3 ** 2 // 256.0
// **演算子は *演算子よりも優先順位が高く、右結合として定義したので
// この式は 0.5 * (2 ** (3 ** 2)) の意味となる

その他[編集]

識別子にはたいていのUnicode文字を用いることができる。

let リンゴの数 = 3
let みかんの数 = 5

文字列リテラルの中にある\(...)には、式の結果が展開される

let リンゴ説明 = "私は\(リンゴの数)個のリンゴを持っている。"  // ”私は3個のリンゴを持っている。"
let 果物説明 = "私は\(リンゴの数 + みかんの数)個の果物を持っている。" //"私は8個の果物を持っている。"

ヒアドキュメントには、ダブルクォーテーション3つを使用する。ヒアドキュメント内の行頭の空白は自動的にトリミングされる。

let isshu = """
            田子の浦に
            うち出でてみれば
            白妙の
            富士の高嶺に
            雪は降りつつ
            """
print(isshu)

数値リテラルのプレフィックスは"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")
}

他言語との比較[編集]

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

外部リンク[編集]