Skip to content

Instantly share code, notes, and snippets.

@dnoguchi
Created May 13, 2012 05:15
Show Gist options
  • Save dnoguchi/2682482 to your computer and use it in GitHub Desktop.
Save dnoguchi/2682482 to your computer and use it in GitHub Desktop.

型のパラメータ化

19.1 関数型待ち行列

この章では,以下のメソッドを持つ関数型待ち行列を構成する。

  • head : 待ち行列の先頭要素を返す
  • tail : 先頭要素を取り除いた形で待ち行列を返す
  • enqueue : 指定した要素を末尾に追加した新しい待ち行列を返す

関数型待ち行列とは,オブジェクトに対する操作を行った際に,既存のデータを変更せずに, 新しいデータを生成して返すものである。

リストとの比較

  1. 共通点
  • 完全永続
  • head, tail をサポートする
  1. 差異
  • 要素の追加 リストは :: を使用して先頭に要素を追加し,関数型待ち行列は enqueue を使用して末尾に追加される。

以降では,関数型待ち行列を効率良く,即ち,head, tail, enqueue の各操作が一定時間で終わるような実装を考える。

リストを利用した効率が悪い実装方法

  1. enqueue が一定時間ではない
  2. head, tail が一定時間ではない

※ 2において,head <-> last, tail <-> init の関係を用いる。

改善した実装方法

先頭から順に要素を並べたリストと末尾から順に要素を並べたリストを用いる。

  • 漸近的計算量ではミュータブルな待ち行列とみなしてよい。
  • head, tail, enqueue の呼び出しがほぼ同じ頻度であることを仮定する必要がある。

2点目の仮定については,head が頻繁に呼び出されると,無駄に Queue を作成するコストの高い処理を実行する必要がある。
この課題の改善策については,最後に取り上げられている。

19.2 情報隠蔽

19.1 で実装したサンプルは,処理効率の点では優秀だが,内部実装を公開してしまっている点が欠点である。
そのための 情報隠蔽 を行う手段について示す。

情報隠蔽方法

  1. 基本コンストラクタに private を指定してコンストラクターを隠す。
  2. 補助コンストラクタを実装する。
  3. コンパニオンオブジェクトにファクトリメソッドを実装する。
  4. インターフェイスを定義したトレイトを使用する。

補足

scala にはグローバルメソッドは存在しないが,グローバルオブジェクトに apply を定義すると,
ファクトリーメソッドをグローバルメソッドのように呼び出して使用することができる。

19.3 変位指定アノテーション

  • 19.2 の最後のサンプルにおける Queue はトレイトであって型ではない。
  • Queue[T] はトレイト,Queue[String] は型。
  • Queue は型コンストラクタ,ジェネリックトレイトとも呼ばれる。

疑問

Queue は共変か?

共変,反変,非変

  • 共変(covariant): 広い型 (double) から狭い型 (float) へ変換すること。
  • 反変 (contravariant): 狭い型 (float) から広い型 (double) へ変換すること。
  • 不変 (invariant): 型を変換できないこと。

via Wikipwdia 共変性と反変性

Scala は標準では,ジェネリック型のサブ型に関して,デフォルトでは非変である。

変位指定

scala においては,型パラメータにプレフィックスを付与することで,変位を指定することができ,
このことを 変位指定 と呼ぶ。

  • 共変

    trait Queue[+T] {...}

  • 反変

    trait Queue[-T] {...}

上記のような変位指定に使用する + や - のプレフィックスを 変位指定アノテーション と呼ぶ。
純粋関数型言語においては,共変であることが自然であるが,ミュータブルなデータを扱う scala の場合,同じようにはならない。

  • サンプル: 非変(厳格)宣言されている Cell クラス

Java の配列は共変

  • 配列をジェネリックに扱う単純な方法のために存在する。
    • sort などのために必要だった。
  • 現在は,型パラメータを使用すればよいので,配列の共変は必要ない。
    • 互換性のために,存在している。

Scala の配列は非変

  • Java と異なり,Scala の配列は非変である。
  • 互換性のために,T型の配列をTのスーパー型の配列に変換する方法も提供されている。

19.4 変位指定アノテーションのチェック

純粋関数型待ち行列においても,変位指定として,共変にはできない。

よくわからないので,省略。。

19.5 下限境界(lower bounds)

下限境界

T, U を型パラメータとし,T は予め定義されているものとする。 U を使用した型パラメータ定義において,U >: T という指定を行うことで, U は T のスーパー型であるという制限を与えることができる。 このとき,T を U の下限境界という。

型駆動設計

  1. 待ち行列を共変にする
  2. enqueue メソッドにおいて,コンパイルエラーが出る
  3. コンパイルエラーが出たメソッドにおいて,下限境界を指定することで修正する

xxx駆動設計というよりも,リファクタリングの手順と言ったほうが適切?

19.6 反変(contravariance)

リスコフの置換原則

T型のオブジェクトx に関して真となる属性を q(x) とする。このときSがTの派生型であれば、S型のオブジェクトy について q(y) が真となる

19.7 オブジェクト非公開データ

サンプル:最適化された関数型待ち行列のサンプル

リスト19.1 のサンプルでは,mirror を呼び出す際に,リストの再構成が行われる可能性があったが,
このサンプルでは,内部の状態を変更する副作用に処理を変えることで,無駄なコピーが行われることを避けている。

19.8 上限境界(upper bounds)

上限境界

T, U を型パラメータとし,T は予め定義されているものとする。
U を使用した型パラメータ定義において,U <: T という指定を行うことで,
U は T のサブ型であるという制限を与えることができる。
このとき,T を U の上限境界という。

サンプル: 上限境界を指定しているマージソート関数

T <: Ordered[T] であるような上限境界を指定した List[T] を引数とするマージソート関数。
ref. 16.6.12 項のリスト 16.1 におけるリストのマージソート関数
ただし,List[Int] のような Ordered[T] を継承していない型を持つリストには使用出来ない。
暗黙のパラメータ,可視境界を使用した拡張方法は,21.6節を参照のこと。

19.9 まとめ

省略

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment