読者です 読者をやめる 読者になる 読者になる

【Swift】Appleの新言語「Swift」のリファレンスを読む(9) - Methods、Subscripts

注意

  • あくまでメモ書きなので細かい部分を端折りますし、色々間違ってるかもしれません。ちゃんとした内容は原文を読んでね。
  • コード例は基本的に原文からそのまま引用していますが、ちょっとした注釈をつけたり、統合したりしています。
  • SyntaxHighlighterが対応してないので微妙に読みにくいです。SyntaxHighlighterはこちらのものを使用させて頂いてます。
  • 他言語にそっくりな部分でも指摘しない。(自戒)

Methodsメソッド

メソッドとはインスタンス(あるいはStructure、Enumeration、Classそのもの)に属するFunctionです。なのでFunctionの各ルールに従わなければなりませんし、Functionの特性はすべて備わっています。

Instance Methods

インスタンスメソッドは、インスタンスを生成することで初めて呼び出せるメソッドです。

正直この辺の話はオブジェクト指向パラダイムに触れたことがあればほっとんど何も話すことがないです。

class Counter {
    var count = 0

    func increment() {
        count++
    }

    func incrementBy(amount: Int) {
        count += amount
    }

    func reset() {
        count = 0
    }
}

let counter = Counter()
// the initial counter value is 0

counter.increment()
// the counter's value is now 1

counter.incrementBy(5)
// the counter's value is now 6

counter.reset()
// the counter's value is now 0

Functionの特性はすべて備わっているので、外部引数名を設定することも可能です。

class Counter {
    var count: Int = 0

    func incrementBy(amount: Int, #numberOfTimes: Int) {
        count += amount * numberOfTimes
    }

}

let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3)
// counter value is now 15

ちなみにこの外部引数名、Overrideして変えることが出来るそうです。その時に外部引数名をアンダースコア(_)にすると、外部引数名そのものをなくすことが出来るそうです。

また、明示的に自分自身にアクセスするためにselfキーワードが用意されています。これが何の役に立つのかは皆さんの想像通りです。

struct Point {
    var x = 0.0, y = 0.0

    func isToTheRightOfX(x: Double) -> Bool {
        return self.x > x
    }
}

let somePoint = Point(x: 4.0, y: 5.0)

if somePoint.isToTheRightOfX(1.0) {
    println("This point is to the right of the line where x == 1.0")
}
// prints "This point is to the right of the line where x == 1.0"

値型(Structure、Enumeration)に設定するメソッドの注意点として、自身のプロパティを変更するようなメソッドを定義する場合はmutatingキーワードをつける必要があります。

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveByX(deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}

var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0)
println("The point is now at (\(somePoint.x), \(somePoint.y))")
// prints "The point is now at (3.0, 4.0)"

また、mutatingとselfを併用することでインスタンスそのものを再生成 / 再定義することが可能です。

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveByX(deltaX: Double, y deltaY: Double) {
        self = Point(x: x + deltaX, y: y + deltaY)
    }
}

enum TriStateSwitch {
    case Off, Low, High
    mutating func next() {
        switch self {
        case Off:
            self = Low
        case Low:
            self = High
        case High:
            self = Off
        }
    }
}

var ovenLight = TriStateSwitch.Low
ovenLight.next()
// ovenLight is now equal to .High
ovenLight.next()
// ovenLight is now equal to .Off

Type Methods

前回説明したPropertyと同じく、型そのものに属する、所謂staticなメソッドを定義することが出来ます。文法もPropertyと大体一緒です。

class SomeClass {
    class func someTypeMethod() {
        // type method implementation goes here
    }
}

SomeClass.someTypeMethod()

当然のことながら、staticなメソッドからアクセスするプロパティはstaticでなければなりません。また、インスタンスメソッドからstaticなメソッドを呼ぶ場合は、selfのコンテクストが同一であれば型名を省略出来ます。

要はいつも通りってことです。

Subscripts(サブスクリプト

Subscriptとは、非常に雑に言うと配列の添え字です。

ArrayであればsomeArray[index]で、DictionaryであればsomeDictionary[key]で値を取得することが出来ます。このindexやkeyの型、そしてこの構文が呼ばれた時の戻り値なんかを、自分で設定することが出来ます。

Subscript Syntax

文法としてはプロパティとよく似ています。「subscript(引数名: 型名) -> 戻り値の型名」って感じです。

subscript(index: Int) -> Int {
    get {
        // return an appropriate subscript value here
    }
    set(newValue) {
        // perform a suitable setting action here
    }
}

Read Onlyな(getしかない)subscriptもプロパティと同様、getの中身だけを記述します。

subscript(index: Int) -> Int {
    // return an appropriate subscript value here
}

シンプルな実装例としてはこんな感じです。

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}

let threeTimesTable = TimesTable(multiplier: 3)
println("six times three is \(threeTimesTable[6])")
// prints "six times three is 18"

Subscript Usage

Dictionaryを例にsubscriptの便利な使い方を紹介しています。大したことは書かれてないので割愛。

Subscript Options

subscriptは別に引数が一つじゃなきゃいけないなんてことはありません。

struct Matrix {
    let rows: Int, columns: Int
    var grid: Double[]
    
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(count: rows * columns, repeatedValue: 0.0)
    }

    func indexIsValidForRow(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }

    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

var matrix = Matrix(rows: 2, columns: 2)
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2

タプルを使って複数の値を返すことも出来るでしょうし、Functionを返してもいいと思います。って言うか、Functionを受け取るのも多分いけるんじゃないでしょうか。