18. Stateful Objects
変化するオブジェクトをモデル化する場合は自然とstatefulになる。
immutableに出来るならそのが望ましい場合が多いが。
scalaではmutableなobjectも制約なく使える。
デジタル回路のシミュレーションのためのDSL(domain specific language)も例上げる。
stateful objectを使う。
DES(discrete event simulation) 離散事象シミュレーション
18.1 What makes an object stateful?
純粋関数的かstatefulかは実装を見なくても振る舞いから分かる。
純粋関数的ならメソッド実行の結果は毎回同じになる。
statefulならそれまでの操作に応じて異なる結果になる。
銀行口座をモデル化したobjectがその例。
varがあればstatefulか?
簡単には判断出来ない。
varがなくても呼び出しているobjectがstatefulならstatefulになる。
varがあっても振る舞いが常に同じなら純粋関数的(stateless)。
計算結果を必要に応じてcacheするような場合は外部からはstatelessに見える。
外部からstatelessに見えればstateless
18.2 Reassignable variables and properties
書き換え可能な変数の基本操作は参照と設定。
non-privateのvarのfieldには自動的にgetter,setterメソッドが追加される。
実際はfieldが内部的なprivate[this]な別名に変換されている。
class X {
var x: Int = _
}
だと
class X {
private[this] var xx: Int = _
def x: Int = xx
def x_=(a: Int) { xx = a }
}
に変換される。
x = a が x_=(a) にコンパイラでsyntactic sugarで解釈される。
private[this]はこのクラスからしかアクセス出来なくしている。
x: Int = _ はxをIntの初期値(0)で初期化している。
x: Int とは書けない。
これはabstract variableの宣言となりclass Xが抽象クラスになる。
自動生成されるgetter, setterのアクセスは元のvarのアクセス制限による。
varがpublicならpublic, varがprotectedならprotected。
getter, setterメソッドさえ定義すれば、実際のfieldの有無はどうでも良い。
使う側からはnon-privateなvarのfieldは直接見えずにメソッドしか見えないので。
def field: T = {...}, def field_=(x: T) {...} が定義されていれば良い。
メソッドのbodyは自由に定義出来る。
意味的には値を返したり、設定している様に見せる必要はあるが。
値のチェックや、別のオブジェクトへの通知や、ログ記録等をやっても良い。
18.3 Case study: Discrete event simulation
stateful objectsとfirst-class function valuesの組み合わせ方法を見る。
デジタル回路のシミューレータの設計と実装。
デジタル回路のためのDSL(言語)。
離散事象シミュレーションのための簡単で一般的なフレームワーク。
シミュレーションモデルの構築のためのライブラリ。
フレームワークを利用したシミュレーションモデルの構築。
Structure and Interpretation of Computer Programs by Abelson and Sussman
これの課題をscheme版からscala版に変更して使う。
18.4 A language for digital circuits
WireとInverter, And-gate, Or-gateのfunction boxからなる。
Wireはtrue, falseの2値のクラス。
各gateは入出力がWireのメソッド。
gateはdelayを持っていて結果のoutputは少し後になる。
gateとWireの組み合わせでより複雑な回路も表現できる。
加算等
各メソッドの名前は通例では動詞だが、今回は名詞。
副作用がある。DSLの特性による。
18.5 The Simulation API
抽象クラスとしてSimulationのフレームワークを定義する。
個々のSimulationはそのサブクラスで定義して実行する。
type Action = () => Unit
型を表すalias。Section 20.6
private var curtime: Int = 0
def currentTime: Int = curtime
curtimeをクラスの外からは参照のみで変更出来なくする。
case class WorkItem(time: Int, action: Action)
caseにするとfactory methodやparameterのaccessorが自動生成されて便利。
nested classについてはSection 20.7
def afterDelay(delay: Int)(block: => Unit) {...}
by-name parameterでblockを評価せずに渡す。
curry化によってbuilt-inのcontrol structureの様に書ける。
(agenda: @unchecked) match { case item :: rest => ... }
このmatchではagendaから先頭要素itemを取り出すことのみを行う。
agendaがemptyにならないと分かっているのでcase _は書いていない。
compilerがwarning: match is not exhaustive!を出すので@uncheckedで抑制。
Section 15.5
18.6 Circuit Simulation
18.5のframeworkから18.4のDSLを定義する。
delayは個々の回路に依存するのでabstraction classとして定義する。
actions foreach (_ ()) // actions: List[() => Unit]
actionsの要素が関数値なので、_ ()で各要素の関数値の関数適用を行うと言う事。
_ () は f => f() の短縮形。関数値を取ってそれを適用する関数値。
val input1, input2, sum, carry = new Wire
input1, input2, sum, carryに対して別々にnewでインスタンスが割り当てられる。
val input1 = mew Wire; val input2 = new Wire;... と同じ意味。
18.7 Conclusion
mutable state and higher-order functions
Mutable state は 状態を持った物理対象のモデルに使える。
Higher-order functions は特定のポイントでのアクションを実施する。
circuit simulationは上記の好例。更に複雑な回路を試してみるのも良い。
0 件のコメント:
コメントを投稿