LL脳がscalaの勉強を始めたよ その63
Scalaコップ本の18章のつづきをやっていきますよー、今回からはステートフルオブジェクトのサンプルとして離散イベントシミュレーションをやっていきますよー
ケーススタディ:離散イベントシミュレーション
デジタル回路のシミュレータをサンプルとして次の3つのポイントを見ていきますよ
サンプルの元ネタは「Structure and Interpretation of Computer Programs」っていう本で元々はScheme用のお題だったみたいですね。今回はこいつをScalaでやってみましょうー、ってのがこの章の目的だったみたいです。それと今回はScala用アレンジとしてサンプルとなるデジタル回路シミュレータを次の4階層にわけてやっていくみたいですねー
基本的な構成として上から順に継承していくような感じになるみたいですねー、ソレじゃやっていきますかねー
デジタル回路のための言語
デジタル回路は「配線」と「ゲート」から構成されるものらしいですね。それぞれ配線が信号を運んで、ゲートは信号を変換するっと…回路なんて情報処理試験ぐらいでしか見ないのでなんだかちょっと新鮮ですなぁ(´・ω・`)ちなみに信号は論理値(true or false)ですね
あと、基本的に論理図は(めんどくさいので)省略するのですが、多分回路図とか見ながらの方が理解しやすそうですね(´・ω・`)
基本ゲート
基本的なゲートとして次の3つがあるですね。この3つをベースにして様々なゲートを組み立てていくみたいです。あれか?XORとかか?名前しか覚えてないですが(´・ω・`)
ちなみに各ゲートにはディレイ(遅延)がかかるそうなので、出力が変わるのは入力が変わってしばらく経過してからだそうです。
デジタル回路のScala表現
デジタル回路の校正用を次のようにScalaクラスと関数によって記述しますYO!っていう目標をズラズラと記述していきますねー
まずは配線は次のようにWireクラスで表現します
// a, b, cの3本の配線を作成します val a = new Wire val b = new Wire val b = new Wire // 省略形としてこんなふうに作成することもできます val a, b, c = new Wire
基本ゲートは次のような手続きを経て作成されるみたいです。
// インバーターを生成する手続きです def inverter(input:Wire, output:Wire) // AND回路を生成する手続きです def andGate(a1:Wire, a2:Wire, output:Wire) // OR回路を生成する手続きです def orGate(o1:Wire, o2:Wire, output:Wire)
ただし上記の各手続きはゲートを(副作用的に)生成するだけでゲートを返すわけではないみたいです。なんだかScala的関数型スタイルではないですが、こうすることで複雑な回路組み立てていけるそう…まあ、先に勧めば理由が分かっていくだろうなので、s機に進みますかねー
ちなみにメソッドの名前がゲート名になっている(普段のメソッド名は大概動詞だけども)のはDSLの宣言的な性質を反映しているからだそうです(´・ω・`)そういうもんか
複雑なゲートの作成例:半加算器
基本ゲートから複雑なゲートを作成する例として半加算器を作成してみますよ。半加算器は2つの入力から、その和と桁上がり(キャリー)を出力するみたいですね。2つの入力をa, b としたら和は(a + b) % 2でキャリーは(a + b) / 2で定義されるみたいです。
論理演算結果だけ書けば次のような感じですかね
a, b | 和, キャリー |
---|---|
0, 0 | 0, 0 |
0, 1 | 1, 0 |
1, 0 | 1, 0 |
1, 1 | 0, 1 |
回路図的には2つのAND回路とそれそれ1つのOR回路とインバーターで構成されるみたいですねwikipedia:半加算器
んじゃ、Scalaで表現しますよー。実際には回路図を先程定義したパーツで置き換えていくだけですねー
def halfAdder(a:Wire, b:Wire, s:Wire, c:Wire){ // 途中の配線を生成します val d, e = new Wire // 各ゲートを回路図にあわせて生成していきます orGate(a, b ,d) andGate(a, b, c) inverter(c,e) andGate(d, e, s) }
複雑なゲートの作成例:全加算器
半加算器を組み合わせて全加算器を作ってみますよー、全加算器の回路図はこんな感じみたいですwikipedia:全加算器。全加算器では2つの入力a, bの他にキャリーイン(cin)という入力をとって和 [ sum = (a + b + cin) % 2 ]とキャリーアウト[ cout = (a + b + c) / 2 ]を出力するみたいです。
論理演算の結果はこんな感じですかねー
a, b, cin | sum, cout |
---|---|
0, 0, 0 | 0, 0 |
0, 0, 1 | 1, 0 |
0, 1, 0 | 1, 0 |
0, 1, 1 | 0, 1 |
1, 0, 0 | 1, 0 |
1, 0, 1 | 0, 1 |
1, 1, 0 | 0, 1 |
1, 1, 1 | 1, 1 |
んじゃ、回路図をベースにScalaで表現しますよー。全加算器は半加算器2つとOR回路1つから構成されるみたいですね(`・ω・´)
def fullAdder(a:Wire, b:Wire, cin:Wire, sum:Wire, cout:Wire){ val s, c1, c2 = new Wire halfAdder(a, cin, s, c1) halfAdder(b, s, sum, c2) orGate(c1, c2, cout) }