いものやま。

雑多な知識の寄せ集め

変種オセロをSwiftに移植してみた。(その2)

昨日の続き。

ボードの実装(続き)

アクションの定義

色に続いて、アクションも列挙型として定義。

  // 続き

  public enum Action {
    case Pass
    case Play(Int, Int)
    case Change(Int, Int)
  }

  // 続く

Swiftの列挙型の面白い機能として、パラメータを取ることが出来るというのがある。
ここではそれを使っている。

例えば、Play(Int, Int)という列挙子が定義されているけれど、これはPlay(1, 1)Play(1, 2)といった列挙子を全て内包した表現になっている。

switch文などで使うときには、パターンマッチを使うことが出来る。

例えば、次のような感じで使うことが出来る。

switch action {
case let Play(row, col):
  // ここでrowとcolを定数として使うことが出来る。
  board = board.play(row, col)
case let Change(row, col):
  board = board.change(row, col)
case Pass:
  board = board.pass
}

クラス変数/定数の定義

続いて、クラス変数/定数の定義。

  // 続き
  
  public static let ROW_MIN = 1
  public static let ROW_MAX = 9
  public static let COL_MIN = 1
  public static let COL_MAX = 9

  private static let Direction = [
    (-1, -1), (-1,  0), (-1,  1),
    ( 0, -1),           ( 0,  1),
    ( 1, -1), ( 1,  0), ( 1,  1),
  ]

  // 続く

クラス変数/定数を定義するには、staticというキーワードをつける。

なお、『詳細Swift』には、格納型のクラス変数/定数は使えないと書かれているけれど、実際にはこのように使うことが出来る。
(Swift 1.2だから?)

ただし、staticというキーワードが示すように、継承したクラスで再定義したりすることは出来ない。

なお、計算型のクラス変数/定数や、クラスメソッドの場合、staticでなくclassというキーワードを使うことで、再定義することが出来る。

プロパティの定義

  // 続き

  private var board: [[Color]]
  public private(set) var move: Int
  public private(set) var turn: Color
  public private(set) var token: Color
  public var opponent: Color {
    return self.turn.opponent
  }

  private var previous: Board?

  private var countCache: [Int?]
  private var legalCheckCache: [Bool?]

  // 続く

クラス変数/定数の次は、プロパティの定義。

基本的にはRubyインスタンス変数としていたものをプロパティとして定義している。
ただ、関連のある計算型プロパティであるBoard#opponentも一緒に定義。

ちょっと脇道に逸れるけれど、このインスタンス変数/プロパティ/メソッドあたりの切り分けの仕方が、どうにも分かりにくいよなぁというのが個人的に思うところ。
このあたり、Rubyはすごくキレイな設計になっているし、C++Java(あるいは昔のObjective-C)もそれはそれで一貫性があるんだけど、Swift(やObjective-C 2.0)はなんかゴチャゴチャしてて分かりにくい。
特に、引数をとらないメソッドがある場合、計算型プロパティにするかメソッドにするかというのはハッキリとした方針が立てられないので、かなりモヤモヤする。

閑話休題
元のRubyのコードと比較すると、キャッシュのためのいくつかのインスタンス変数が削られている。
(具体的には、@playable_places_cache@changeable_places_cache、それと@board_hash_cache
これは、遅延格納型プロパティというのを使っているから。 遅延格納型プロパティについては、また後述したいと思う。

アクセス制御の話

メモリ上にあるデータに誰でも触れてしまえるのでは、その値がいつ誰によって書き換えられてしまうのか分からないため、プログラムを書くのが非常に困難になる。
そこで、データにアクセスできるオブジェクトを制御しようというのがアクセス制御の考え方で、具体的には、プロパティや関数、メソッドなどに可視性を設定して、誰がそのデータにアクセスできるのかを明確にする。

Swiftの場合、可視性としてpublic/internal/privateが用意されていて、

  • publicにすると、誰でもアクセスできる
  • internalにすると、モジュール内からはアクセスできる
  • privateにすると、ファイル内からはアクセスできる

となっている。

さらに、プロパティについてはinternal(set)/private(set)という可視性も用意されていて、これらを指定すると、セッターの可視性をゲッターよりも低くすることが出来る。

上記のプロパティの定義でpublic private(set)なっているのはこれで、「ゲッターはモジュール外にも公開するけれど、セッターはファイル外にすら公開しない」ということを意味する。
Rubyattr_readerが呼び出されている状態に近い)

なお、計算型プロパティのBoard#opponentにはpublicしか書かれていないので、ゲッターもセッターもモジュール外に公開されているように見えるのだけど、実際にはこのような書き方をした場合、セッターが定義されていないので、セッターを呼び出すことは出来ない。
オォ、ヤヤコシイ。。。

今日はここまで!