年を越す前にScala ツリーを解析してみた
Merry Christmas | Cake Solutions Team Blog
Scala初心者の自分には仕組みも去ることながら、文法や作法もとっても勉強になったのでメモ。
def main(args: Array[String]) { \-/. -->*<-- . /.\ ./.|.\. /.oxo.\ ./.*.|.x.\. /.oo.|.oo.\ ./.oxo.|.***.\. /.*.oo.|.*.oo.\. ||| }
仕組み
メソッド|||
実際に処理を行っているのはこの部分。
\.|||
中身は以下のとおり。
@tailrec final def |||(sparkle: Boolean) { val f = (t: Tree) => t match { case _: LeftNeedle => "/" case _: RightNeedle => "\\" case _: Trunk => "|" case _: Ball => "o" case _: Spike => "x" case _: Candle => "*" case _: BigBall => "oxo" case _: DoubleBall => "oo" case _: ElectricCandle => "***" } def walk(t: Tree, depth: Int): List[String] = { def walkLevel(t: Tree, acc: String, f: (Tree) => String): (Tree, String) = { val fx = (t: Tree) => if (sparkle) f(t).toUpperCase else f(t) t match { case l: LeftNeedle => (l.left, fx(l) + "." + acc) case t: Tree => walkLevel(t.left, fx(t) + "." + acc, f) } } t match { case r: RightNeedle => val l = walkLevel(r, "", f) l._2 +: walk(l._1, depth + 1) case s: Star => List("-->*<-- ", "\\-/.") } } val tree = "||| " +: walk(this, 0) tree.reverse.foreach({l => val numSpaces = 30 - (l.length() / 2) val padding = " " * numSpaces print(padding) println(l) }) Thread.sleep(500) |||(!sparkle) }
val tree = "||| " +: walk(this, 0)
+:はSeqトレイトの加算メソッド。この場合は文字列"||| "にwalkメソッドの返り値Listを結合したListを返す。
walkメソッドは再帰して、行ごとの文字列をもったリストを返す。
tree.reverse.foreach~
Listの順番が根っこ(|||)からになっているので、リバースして、パディングして、出力。
val f = (t: Tree) => t match {
パターンマッチを行うメソッド。型がマッチしたら対応する文字列を返す。
def walkLevel(t: Tree, acc: String, f: (Tree) => String): (Tree, String)
一行ごとの処理メソッド。LeftNeedleが見つかるまで再帰する。|||の引数の真偽によって大文字にしたり小文字にしたりして点滅を演出。メソッドの外側のスコープで定義したfをそのまま使っているだけなので、f: (Tree) => String)はなくても良かった。
def walk(t: Tree, depth: Int): List[String]
なかでwalkLevelを回しているメソッド。walkLevelの返り値のタプルから文字列をリストに格納、クラスを次のwalkLevel関数に渡している。walkLevelが帰ってくるたびにdepth+1をしているが、実際にはdpeth:Intは使っていない。