プログラミングHaskellをScalaで解く(その5)
引き続き、『プログラミングHaskell』の例題と練習問題をScalaで書いてみる。
プログラミングHaskell |
P.113 ライフゲーム
『プログラミングHaskell』では、ライフゲームは「第9章 対話プログラム」内の例として挙げられる。
val width:Int = 5 val height:Int = 5 type Pos = (Int, Int) type Board = List[Pos] val glider:Board = List((4,2), (2,3), (4,3), (3,4), (4,4))
盤のサイズと、セルの位置を示すためのPos型を定義した。
gliderは『プログラミングHaskell』で定義してある、グライダー配置そのものである。
def goto(p:Pos):Unit = print("\u001B[" + p._2 + ";" + p._1 + "H") // P.110 // \u001B == \ESC def writeat(p:Pos, xs:String):Unit = { // P.110 goto(p) print(xs) }
Haskellでは、"\ESC"と書けばESCを表わす制御文字となるのだが、Scalaではエスケープシーケンスを上手く処理してくれなかったので、Unicodeで記述した。
def showcells(b:Board):Unit = for(p <- b) writeat(p, "O") def isAlive(b:Board, p:Pos):Boolean = b.exists(_ == p) def isEmpty(b:Board, p:Pos):Boolean = !(isAlive(b, p)) def wrap(p:Pos):Pos = ( ((p._1-1)%width)+1, ((p._2-1)%height)+1) def neighbs(p:Pos):List[Pos] = { val (x,y) = p List((x-1,y-1),(x,y-1),(x+1,y-1),(x-1,y), (x+1,y),(x-1,y+1),(x,y+1),(x+1,y+1)).map(wrap(_)) } def liveneighbs(b:Board,p:Pos):Int = neighbs(p).filter(isAlive(b, _)).length def survivors(b:Board):List[Pos] = for(p <- b; if List(2,3).exists(_ == liveneighbs(b,p))) yield p def births(b:Board):List[Pos] = for (x <- (1 to width).toList; y <- (1 to height).toList; if isEmpty(b,(x,y)); if liveneighbs(b,(x,y)) == 3) yield (x,y) def rmdups[A](l:List[A]):List[A] = { l match { case Nil => Nil case (x::xs) => x :: rmdups(xs.filter(_ != x)) } } def nextgen(b:Board):Board = survivors(b) ::: births(b) def cls:Unit = print("\u001B[2J") // P.109 def sleep(n:Int):Unit = Thread.sleep(n) def life(b:Board):Unit = { cls showcells(b) sleep(1000) life(nextgen(b)) }
wait関数として、java.lang.Thread.sleep() を利用した。但し、waitという名前にすると java.lang.Object.wait() として定義されている関数と名前が衝突するので、sleepとした。
scala> life(glider)
今回はここまで。