Scalaっぽく迷路をリファクタリングしてみる(穴掘り法)
この記事は前記事
※Scalaで迷路を作ってみる(穴掘り法) - AccessViolation Exception
のリファクタリングとなります
Scalaっぽく?
- 別にこの実装ならc#で書けんじゃん
match
,implicit
ぐらいしか使ってなくね- むしろvalとvar周りでうまく設計できてなくね
- というか
map1 intersect map2
どうやって実装すんのかわかんねぇ
- 調べるとすごく便利そうな機能あんじゃん
- 特に型周りに境界が設けられる
- 暗黙の~的なもの
- そういえばc#の拡張メソッド的なものはどうやるんだろうか
static class IntExtension { public static int Twice(this int x) { return x * 2} }
を定義すると
100.Twice()//200
と出来るやつ
とc#でできることができないみたいなことに腹を立てたのでリファクタリングしました。 参考にしたのは某書籍と
Scala 2.10.0 M3の新機能を試してみる(3) - SIP-15 - Value Classes - kmizuの日記
こちらです。大変参考になりました。
ともあれやってみる。以前のコードは前記事を参考に
(c#で言うところの) 拡張メソッドを実装したい
printMaze(dst)
はださい。駅ホームの階段で転ぶくらいださい
implicit classでメソッドを追加する
implicit class Maze(val self:Map[(Int,Int),String])
を定義してメソッドを追加することでMap[(Int,Int),String]型から呼ぶことを有効化できる
ただMaze型のクラスメソッドとなってしまうのでMap[(Int,Int),String]->Mazeの暗黙の型変換を用意してやると
implicit def mapToMaze(self:Map[(Int,Int),String]) : Maze = new Maze(self)
型変換が必要なときに勝手に読んでくれるのでmapToMaze(dst)
を自分でやる必要はない
そんな感じでdst.draw
で迷路が出力できるようになりました。
同じ要領で前回困っていたMapのintersectも
intersect(src,dig(arounds.head,src)(size))
もsrc intersect dig(arounds.head,src)(size)
とできるようになった。
immutableな実装にする
関数型とは
純粋関数型言語では、参照透過性が常に保たれるという意味において、全ての式や関数は副作用を持たない。
副作用のない実装といえば状態によって結果が変わらない。例えば円周はいかなる状態であれば2πrのようなもので内部に状態を持ってはいけない*1
既存の実装だとdig内のsrc,aroundsが状態を持っているので排除してみる
今まで
- 今いる場所の4方を列挙
- 4箇所全てに対して
- その場所の壁を壊していいなら壊す
around filter(canDig) foreachで実装しなかったのはそこまでの結果でcanDigの結果が変化してしまうから。filterを先にしてしまうと結果が保証できなくなってしまう*2
リファクタリング後
- 予め関数が壊す場所のリストを受け取る
- 無いなら壊す場所のリストを列挙する
壊す場所のリストの先頭が壊せる
- こわす
- 今の場所から他の場所に伸ばせるなら伸ばす
壊せない
- 壊す場所のリストがまたあるならそこを壊してみる
- ないなら終わり
に変えれば自己完結できてかつvarを排除することができた
まだまだ雑だけどもっと効率よく美しいコードを書いていきたい。