2014年6月8日日曜日

Swift言語の入門 (7) クラス編

さてさてお待ちかね、Swiftにおけるクラスの扱いについていろいろと見ていこうと思います。Objective-Cでは、プログラムを書く上で、必要なコンポーネントは大体クラス化されており、一部C言語由来のものなどに対して構造体を使ったり、定数のまとまり等に列挙を使う程度のイメージでしたが、Swiftではそのバランスが大きくことなりそうです。

Swift言語の入門 (2) 列挙型編Swift言語の入門 (5) 構造体編などで説明したとおり、これら二つの要素もクラスに引けを取らない機能を備えており、かつクラスとは全く異なる特徴を兼ね備えています。

この章ではこれらの違いも確認しながら、クラスの定義から利用方法までをひととおりみていきます。

1.宣言


もっとも小さいクラスの宣言は、以下の通りです。

class Player{
}

宣言と実装のように記述を二つ書く必要はなく、ただ単にクラスキーワード、クラス名、中括弧で終わりです。

プロパティや関数を宣言するには、中括弧の中にかきます。

struct Point {
    var x:Double
    var y:Double
}

class Player{
    var position:Point = Point(x:0, y:0);
    func moveTo(p:Point){
        position = p;
    }

}

このPlayerクラスは、positionという名前の座標情報を保持するプロパティを持っており、デフォルトでは(0, 0)です。moveToという関数はプレイヤーを任意の位置に移動させる関数です。構造体のときには、mutating修飾子が必要でしたが、クラスは基本的に内部状態を変えながら動くものなので、クラスで宣言される関数にmutating修飾子をつける必要はありません。そのため、内部状態を頻繁に変えるようなものにはクラスを用いて、そうでないものには構造体を採用するというのが自然な使い方になります。

ここではpositionに初期値を設定してありますが、設定しないとコンパイルエラーになります。その理由は、現状Playerクラスはイニシャライザ(コンストラクタに相当)を持っていないためpositionが初期化されず、またpositionの型はPoint型でnilを受け付けないからです。なので、Point型の最後に「?」や「!」をつけてオプションにするか、イニシャライザの中でpositionを初期化すればコンパイルエラーは起きなくなります。

2.インスタンスの生成


クラスのインスタンスを生成するには、ただ単にクラス名の後に「()」をつけ、引数を記述します。それぞれの引数には変数名をつける必要があります。

var player = Player(position:Point(x:1, y:2))

これらの変数名は、外部変数名と呼ばれ、通常イニシャライザの内部処理を記述するときにそのまま内部変数名として使われますが、以下のように書くことで、外部変数名と内部変数名を別の文字列にすることが可能です。

init(position p:Point){
    self.position = p

}

positionが外部変数名で、pは内部変数名になります。引数に外部変数名をつけられるという仕様はSmalltalkからObjective-C、そしてSwiftへと受け継がれてきており、コードを読みやすくすると同時に、外部変数名の違いによる関数のオーバーロードを行う等、他の言語にはない独特の特徴を実現しています。

3.関数の呼び出し


クラスのインスタンスの持つ関数を呼び出すには、構造体や列挙型とおなじく、「.」のあとに関数名を書きます。

player.moveTo(Point(x:100, y:-100))

引数の外部変数名は、関数側で明示的に書かない限り、呼び出し時には指定せず、内部変数名を外部変数名として指定した場合には、コンパイルエラーになります。

4. クラスの継承


クラスを継承して新たなクラスを作成するには、以下のようにクラス名の後に「:」を書き、その後に親クラスの名前を書きます。継承はクラスのみに許されている機能で、他のクラスの機能を受け継ぎながら、新たな機能を追加し再利用するための機構です。

class RoboPlayer : Player{
    init(){
        super.init(position:Point(x:6, y:0))
    }
    
    override func moveTo(p:Point){
        //ロボはたまに言うことをきかない、とか
    }
}

子クラスは、自動的に親クラスの持つイニシャライザを継承します。また、親クラスの持つ関数をオーバーライド(上書き)したいときには、override修飾子をつける必要があります。これにより、うっかり全く同じ名前の関数を作ってしまうという問題を防いでいます。

子クラスのイニシャライザでは、親クラスのイニシャライザを呼び出す必要があります。

5.デイニシャライザ(Deinitializer)


クラスのインスタンスが解放されるときには、デイニシャライザと呼ばれる特別な関数が実行されます。デイニシャライザは引数をとらず、書き方は以下の通りです。

class DeinitialzerTest{
    deinit{
        
    }
}

総括

2014年6月5日木曜日

Swift言語の入門 (6) オプション型編

Swiftの言語仕様を読んでいくと、はじめにみつかる特徴的な仕様に、オプション型があります。オプション型とは、名前の通り、必ずしも存在する訳ではない、と言った意味合いがあり、その型そのものの値を含むか、もしくはその値が存在しないことを意味する型です。

他の言語ではNullableなどと呼ばれていることもありますが、Swiftでは全ての型にオプション型が用意されており、IntやDoubleに対しても利用することができます。

JavaではNullableというタイプアノテーションがありますが、これはプリミティブ型(値型)には対応しておらず、C#のNullableは逆に値型にのみ対応しています。Swiftではどちらの種類の型のオプション型を作ることができるため、IntやDoubleなどの型や、独自のクラスのためのオプション型を作ることができます。

1.宣言


var optionalName: String? = "John Appleseed"

オプション型の宣言は、ただ単に型の最後に「?」を追加するのみです。これをつけることにより、optionalNameにnilを代入することができるようになります。
Swiftでは、オプション型でない方にはnilを代入することができず、コンパイルエラーとなるので、逆を言えばオプション型出なければ必ず値は存在しており、nilかどうかチェックする必要は全くありません。

2.オプション型のアンラップ


オプション型からオプション型でない元の型をアンラップ(unwrap)ためには、いくつかの方法があります。

2.1 if let 構文によるアンラップ

if let name = optionalName {
    greeting = "Hello, \(name)"

}

このように、if文の条件の中にlet文を書くことで、「optionalNameが値を持っていた場合には、その値をnameという変数に代入して { ... } 内を実行する」という意味のコードがかけます。optionalNameが値を持たなかったときの処理を書きたいときには、elseの中に書くこともできます。

2.2 「!」演算子によるアンラップ

また、オプション型として宣言したけれども、nilではない値を代入した直後等のような、プログラマの目から見て明らかな場合に関しては、

greeting = "Hello, \(optionalName!)"

のように、変数の末尾に「!」をつけることで強制的に元の型に変換することができます。
ただし、この方法を利用して取り出した値がnilであった場合にはランタイムエラーが起きてしまうので、使う際には注意が必要です。しかしその分いちいちnilかどうかのチェックを行わないので、nilでないことが明らかな場合には、オーバーヘッドを少なくすることが可能です。

2.3 「?」演算子によるアンラップ

メソッドチェーン等を行う際に重宝する書き方ですが、

optionalName?.isEmpty

と書くことで、optionalNameに値があった場合は全体としてisEmptyの戻り値に、nilであった場合は全体としてnilになるという書き方もできます。

この式が評価されるときには、内部的にoptionalNameがnilかどうかを判定して、nilであればその場でnilと評価し、そうでなければそのまま計算を続けるような処理がなされます。

もともとObjective-Cではnilに対するメソッド呼び出しは無視されるという仕組みが備わっていたので、その機構とほぼ同一のものとしてみてよいでしょう。

3.暗黙的にアンラップされるオプション型


元の名はImplicitly Unwapped Optionalsですが、すなわち勝手にアンラップを行ってしまうようなオプション型、ということです。挙動としてはObjective-Cの通常の変数のように、nilも入るし値もはいる型で、nilをアンラップしてしまったときはクラッシュするという型です。挙動的にはJavaの参照型のようなイメージですが、これは何のために必要なのでしょうか。

宣言方法は、「?」のかわりに「!」を型の最後に書くだけです。

  • let assumedString: String! = "An implicitly unwrapped optional string."

使い方としての違いは、値を利用する際に今まで必要だったアンラップの処理がいらないことです。

println(assumedString)

もちろん明示的にif let文を使ってアンラップすることも可能です。

  • if let definiteString = assumedString {
  • println(definiteString)
  • }

このタイプのオプション型の場合には、「!」によるアンラップと同様に、逐一nilのチェックを行わないため、オーバーヘッドが「?」のオプション型よりも小さくなります。

これだけでは使いどころがさっぱり不明ですが、これは主にARCのリファレンスカウンタにおける循環参照の問題を処理するために導入された仕様のようで、あえて「!」にすることによりアクセスのためのオーバーヘッドを減らしつつ、初期化の際に一瞬nilの瞬間を作り出すことによって、循環参照の問題を起こさなくすることができるそうです。詳細はまた別の回にて行いたいと思います。

総括

  • オプション型は全ての型に対して作れる
  • 例外的値等を処理する際に重宝する
  • 「?」はnilだったら無視される
  • 「!」はオーバーヘッドが無いかわりにnilだとクラッシュ
いらない情報
オプション型のオプション型も作れるようで、String!?ともかけたりするようです。

Swift関連記事

2014年6月4日水曜日

Swift言語の入門 (5) 構造体編

Swift言語の構造体は、C言語の構造体からパワーアップし、クラスとも異なる新しい機構へと変化しました。クラスと少々紛らわしいですが、大きな違いは変数へ代入するときの挙動で、クラスは参照渡し、構造体は値渡しです。その特徴も相まって、一度作った構造体の値を変更する関数を書くときには、mutating修飾子を指定しなければならない等、今までに無かったルールが定められています。

1.宣言

  • struct Point {
  • var x: Double
  • var y: Double
  • func norm() -> Double {
  • return norm(x * x + y * y)
  • }
  • }

C言語の構造体とは異なり、メソッドも定義できるようになりました。これでわざわざ第一引数に構造体をとって何かするような関数を大量に生成する必要がなくなりました。

2.初期化

var p = Point(x:1, y:2);

構造体を実際に利用するには、上記のような書き方をします。メンバーの名前とコロンと値をカンマで繋げていく方式です。newなどのキーワードは特にありません。

現状このPoint構造体には、フィールドの初期値が指定されていないため、引数無しでPointを作ろうとするとエラーになります。しかしながら、構造体の宣言の際に、各フィールドに初期値をあたえておけば、引数無しでも初期値を使って構造体を初期化することが可能です。

3.フィールドへのアクセス

フィールドへのアクセスは通常通り、ドット演算子を用いて行います。

println(p.x);

また、フィールドへの代入も同様です。

p.x = 10;

構造体が値渡しである証拠に、一度別の変数に代入してしまうと、その後元の変数が書き換えられても、代入先には影響を及ぼしません。

var p = Point(x:1, y:2);
var q = p;
p.x = 10;

println("p.x = \(p.x), q.x = \(q.x)") //p.x = 10.0, q.x = 1.0


4.mutating

構造体の値を書き換える関数を書くときには、mutating修飾子をつける必要があります。
例えば、Point構造体の位置をY軸にそって反転させるflipY関数を例にとります。

func flipY() {
    x = -x //この = でエラー

}

このままではエラーが表示され、コンパイルできません。なので、funcの前のmutating修飾子をつけて再度コンパイルをします。

mutating func flipY() {
    x = -x

}

総括
  • 構造体は値渡し
  • 関数も持てる
  • 構造体を書き換える関数にはmutating修飾子
Swift関連記事

Swift言語の入門 (4) パターンマッチ編

Swiftに導入されたモダンな機能の一つに、パターンマッチがあります。これは、switch文などのなかで、変数の状態によって場合分けを行うために多く用いられます。
if文をふんだんに使えばもちろん同等な機能は実現できはするのですが、パターンマッチを使うことでよりシンプルで明確に記述することができるようになります。

1.ワイルドカードパターン

  • for _ in 1...3 {
  • // Do something three times.
  • }

たとえば、for文で何らかの処理を3回行いたいけれども、現在が何回目かどうかを気にする必要がないような場合には上記のコードを書きます。1...3は[1, 2, 3]を表し、中の点が3つなので3も含むことに注意してください。

2.値束縛パターン

  • let point = (3, 2)
  • switch point {
  • // Bind x and y to the elements of point.
  • case let (x, y):
  • println("The point is at (\(x), \(y)).")
  • }

switch分のcaseが一つしかありませんが、その中でpointの3と2をそれぞれxとyに割り当てています。

3.タプルパターン

  • let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
  • for (x, y) in points {
  • /* ... */
  • }

for...inの中で、それぞれの要素に対してパターンマッチを適用する際に利用します。
タプルの要素は全て変数である必要があります。


  • let point = (1, 2)
  • switch point {
  • case (0, 0):
  • println("(0, 0) is at the origin.")
  • case (-2...2, -2...2):
  • println("(\(point.0), \(point.1)) is near the origin.")
  • default:
  • println("The point is at (\(point.0), \(point.1)).")
  • }

数値の範囲等の条件を指定してパターンマッチを行います。switch文の中でのみ利用可能です。

  • // Overload the ~= operator to match a string with an integer
  • func ~=(pattern: String, value: Int) -> Bool {
  • return pattern == "\(value)"
  • }
  • switch point {
  • case ("0", "0"):
  • println("(0, 0) is at the origin.")
  • case ("-2...2", "-2...2"):
  • println("(\(point.0), \(point.1)) is near the origin.")
  • default:
  • println("The point is at (\(point.0), \(point.1)).")
  • }

式のマッチングオペレータは ~= で表され、これをオーバーロードすることによって、独自のマッチング機構を作り出すことができます。


総括
  • Swiftではswitch文の出番がものすごくおおい
  • パターンマッチを使って型を分解しよう
  • 式のマッチングオペレータはオーバーロードできる
Swift関連記事

2014年6月3日火曜日

Swift言語の入門 (3) コレクション編

SwiftにはArrayとDictionaryの2つのコレクション型があります。

1.Array

  • var shoppingList = ["Eggs", "Milk"]

このように、角括弧で囲い、カンマ区切りで要素を記述していきます。JSONにおける配列の記述の方式と同様です。

また、String型のArrayはArray<String>型となりジェネリクス型として表現されます。これの略記法としてString[]というJavaの配列のような書き方も認められています。

  • println("The shopping list contains \(shoppingList.count) items.")
  • if shoppingList.isEmpty {
  • println("The shopping list is empty.")
  • } else {
  • println("The shopping list is not empty.")
  • }

ArrayはcountやisEmptyといったObjective-Cでも存在していたプロパティを持っています。しかしながら、Objective-Cとの大きな違いとして、変更可能性は配列の型によらず、変数であるかもしくは定数であるかに依存します。すなわち、letで宣言された配列は変更不可で、varの場合には変更が可能です。

配列変数は変更可能であるので、たとえば

  • shoppingList.append("Flour")
  • shoppingList += ["Chocolate Spread", "Cheese", "Butter"]

などと書くことで、配列に要素を追加することができます。


各要素へのアクセスは、従来通り

  • var firstItem = shoppingList[0]

のようにして行い、範囲生成と組み合わせることで、

  • shoppingList[4...6] = ["Bananas", "Apples"]

のように配列の中の特定の範囲をごっそり入れ替えることができます。この例では、4, 5, 6番目(ドットが3つあるので6を含む)の要素を["Bananas", "Apples"]で差し替える(要素の数は一つ減る)、という処理になります。

配列はObjective-CのFast Enumerationのように

  • for item in shoppingList {
  • println(item)
  • }
と書くことで走査することができます。

配列変数を別の変数に代入するときには、基本的には参照が渡され、実体は同じものを参照することになります。ただし、配列の長さを変更するような処理を行ったときに初めて、元の参照している配列からコピーされ、新たな配列が生成されます。

2.ディクショナリ

いわゆる連想配列で、キーとなる型と値となる型の二つをペアにして保持することのできる型です。

  • var airports: Dictionary<String, String> = ["TYO": "Tokyo", "DUB": "Dublin"]

宣言は上記のように行い、ジェネリクスの角括弧を用いてキーと値の型を指定します。
JSONの表記法と似ているようで違うのは、JSONの場合は括弧が[ ] では無く{ }であることくらいでしょうか。

また、型推論を利用して、ジェネリクスの型注釈を省くことも可能です。

値へのアクセスは


  • airports["LHR"] = "London"

のように[ ] の中にキーを記述することで行います。

if letの構文を利用することで、キーがあるかどうかをチェックした上で、その値に関する処理を記述することができます。

  • if let airportName = airports["DUB"] {
  • println("The name of the airport is \(airportName).")
  • } else {
  • println("That airport is not in the airports dictionary.")
  • }

ディクショナリの走査にはFast Enumeration式の方法が利用でき、タプルの形式でキーと値を束縛して処理を書きます。

  • for (airportCode, airportName) in airports {
  • println("\(airportCode): \(airportName)")
  • }

ディクショナリ変数を他の変数に代入する際には、全て参照ではなく値が渡されます。そのため、ディクショナリを代入した後に、もとのディクショナリを編集しても、代入後のディクショナリはもとのまま変化がありません。


総括

  • 配列は基本参照渡し、長さを返るタイミングでコピー
  • ディクショナリは値渡し
  • for .. in で走査

Swift言語の入門 (2) 列挙型編

早速ですが横道にそれていきたいと思います。個人的にSwift言語で「これは!」と思ったポイントに、列挙型の強化があります。いままでObjective-Cでは、定数をまとめてたくさん変数するための構文、程度の認識が強かった列挙型ですが、Swiftではメソッドを持つことができるようになる等、大幅に強化されました。

1.列挙型

  • enum CompassPoint {
  • case North
  • case South
  • case East
  • case West
  • }

これはシンプルなサンプルですが、方位を列挙するためのenumです。それぞれの値は、CやObjective-Cとは異なり、int型が勝手に割り当てられたりすることはありません。

  • switch directionToHead {
  • case .North:
  • println("Lots of planets have a north")
  • case .South:
  • println("Watch out for penguins")
  • case .East:
  • println("Where the sun rises")
  • case .West:
  • println("Where the skies are blue")
  • }

もちろんswitch文を使って分岐処理を書くことができます。

他にも、列挙型自体が関数を持つことができ、値のそれぞれを一つのオブジェクトのように扱うことができるようになりました。

  • enum Rank: Int {
  • case Ace = 1
  • case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
  • case Jack, Queen, King
  • func simpleDescription() -> String {
  • switch self {
  • case .Ace:
  • return "ace"
  • case .Jack:
  • return "jack"
  • case .Queen:
  • return "queen"
  • case .King:
  • return "king"
  • default:
  • return String(self.toRaw())
  • }
  • }
  • }


2.Associated Value

ここから先はただの進化というレベルではないものになります。関数型プログラミング言語ではいくつか採用されている、代数的データ型に相当するものがSwiftでもサポートされています。 

  • enum Barcode {
  • case UPCA(Int, Int, Int)
  • case QRCode(String)
  • }

これは、バーコード型はUPCAという(Int, Int, Int)と紐づけられる値か、QRCodeというStringと紐づけられる値のどちらかを持つ、ということを表しています。

つまり、二つの異なる形式のバーコードであるにもかかわらず、自然に同じ型として扱うことができるのです。

これらをそれぞれの値に応じて適切に処理するためには、パターンマッチングを利用して、以下のように書きます。

  • switch productBarcode {
  • case .UPCA(let numberSystem, let identifier, let check):
  • println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
  • case .QRCode(let productCode):
  • println("QR code with value of \(productCode).")
  • }

この機能により、「何らかの処理を行って成功した場合には結果を返し、失敗した場合にはエラーを返すような関数の戻り値の型」を自然に表現することができるようになります。また、switch文に書ける際に強制的にエラー処理の分岐も書かせることになるため、エラーが起きているのにも関わらず正常系のルートを通ってしまう、と行ったバグを起こす心配がありません。

総括