Scalaプログラミング5 MapとStackを使ってみる
Beginning Scala programing5
初心者ぺちぱーがGitHubでScalaレッスンを始めたぞ。5回目はコレクションクラスのMap
とStack
を使ってみよう。
引き続き、練習問題はここのページだ。今回はハノイの塔を攻略してみようと思う。
Lessen 5: Tower of Hanoi
再帰関数を使ったアルゴリズムで有名な問題。
Wikipediaのハノイの塔を見てみよう。
以下のルールに従ってすべての円盤を右端の杭に移動させられれば完成。
3本の杭と、中央に穴の開いた大きさの異なる複数の円盤から構成される。
最初はすべての円盤が左端の杭に小さいものが上になるように順に積み重ねられている。
円盤を一回に一枚ずつどれかの杭に移動させることができるが、小さな円盤の上に大きな円盤を乗せることはできない。
円盤が1枚の場合はA->Cと移動しておしまい。
円盤が2枚の場合は
- 1をA->B
- 2をA->C
- 1をB->C
円盤が3枚の場合は
- 1をA->C
- 2をA->B
- 1をC->B
- 3をA->C
- 1をB->A
- 2をB->C
- 1をA->C
と移動すればいい。ポイントとなるのは、
- Aにある1からn-1までの円盤をAからBに移動
- 円盤nをA->Cに移動させる
- Bにある1からn-1までの円盤をBからCに移動
手順となるところだ。円盤nを、AからBを経由してCに移動させる、という操作をmove(n, a, b, c)
で定義できるのであれば、上記の操作は次のように書けるはずだ。
n=2の場合、1の操作が、n=3の場合、1-3の操作がmove(n-1, a, c, b)
(Aにある1からn-1までの円盤をAからBに移動)に相当する。n=2の場合、3の操作が、n=3の場合、5-7の操作がmove(n-1, b, a, c)
(Bにある1からn-1までの円盤をBからCに移動)に相当するわけだ。
というわけで、最初はこんなクラスを書いてみた。
n=2の場合。
n=3の場合。
よし。できてそうだな。
Visualize pegs state using Map and Stack
次は、移動中の円盤の状態を可視化するために、杭をStack
と見立てて、状態をもたせてみよう。
Stack
円盤の出し入れは、first in last outなので、杭の状態はStack
で表現してみよう。円盤の出し入れをするために、今回はmutable.Stack
を使う。円盤の大きさは番号で表現するので、Int
にしておく。
Map
次は、杭のラベルとStack
を関連づけるために、Map
を使ってみよう。
Constructor
今までは、object
で宣言してシングルトンにしていたけれど、杭の状態を保持するために、円盤の枚数を与えられると、その枚数の円盤をStack
にpushできるクラスを作ってみよう。
Scalaのコンストラクタは、クラス定義の先頭に書くことになっている。このコンストラクタをprimary constructor
(基本コンストラクタ)という。こう書いた場合、num
とpegs
がクラスメンバーとして定義され、コンストラクタ引数が自動的にメンバーにセットされるようになる。メンバーはval
となっているため、setterアクセスはできず、getterのみアクセス可能となる。private val pegs
と書くと、外部からのアクセスはできなくなる。setter/getterは、わざわざ定義しなくても、メンバーにアクセスできるようになっている。
Constructor overload
pegs
は内部で生成するようにしたいので、別のコンストラクタも定義しておこう。コンストラクタはthis()
で定義する。基本コンストラクタ以外に定義したコンストラクタはauxiliary constructor
(補助コンストラクタ)という。
コンストラクタをオーバーロードする場合、基本コンストラクタ、または定義済みの補助コンストラクタを最初に実行しなけらばいけない。コップ本のp.111-p.116に記載がある。この辺は、Scala referenceの5.3 Class Definitionsの方が詳しい記載があるかもしれない。~~今でしょ!~~あとで読もう。
Implementation
Initial disks
インスタンス生成時に、pegs
に円盤を追加しておこう。
Disk operation
円盤を移動する操作は、Stack
のpush()
とpop()
メソッドで行う。ハノイの塔のルールでは、小さい円盤の上に大きい円盤を乗せることはできないので、違反した場合には例外を投げるようにしてみた。IllegalArgumentException
でいいのかな。どうなんだろ。
Print Stack status
状態の表示はこんな感じ。
State class
全体としてはこんな感じになった。
Solver
State
クラスを使って、SimpleHanoiSolver
を修正したクラスはこんな感じ
Results
実行してみると、こんな感じで出力できた。ふむふむ。いい感じなんじゃないか!?お絵描きできるともっと分かりやすくなるのかなと考えると、出力部分は、別のPrinterHogeHoge
クラスにした方が良かったかもしれない。
n=2の場合。
n=3の場合。
Conclusion
5回目のレッスンもGitHubに上げてある。今回はクラスを作ってみたり、Stack
とMap
を使ってみたりした。まとめ!
class
の作り方- コンストラクタの使い方。基本コンストラクタと補助コンストラクタ
Stack
とMap
の基本的な操作
Reference
- Scalaスケーラブルプログラミング第2版
- 作者
- Martin Odersky
- Lex Spoon
- Bill Venners
- 出版社
- インプレスジャパン
- 発売日
- 2011-09-27
- メディア
- 単行本(ソフトカバー)
- 価格
- ¥ 4,968