似非プログラマのうんちく

「似非プログラマの覚え書き」出張版

Scala でとことん FizzBuzz する(その 4)

最初にお詫び。前回の記事にかなり大幅な修正を加えています。再度確認をしていただけるとありがたいです。

追記 : 今回作ったソースファイル群(+ テストコード)を GitHub で公開しました。
akaneko3/FizzBuzzScala · GitHub

分岐アルゴリズム再考(続き)

その 3 : Map を利用する

RubyHash を利用して作られたものにヒントを得て作ったもの。

package jp.mydns.akanekodou.scala.fizzbuzz

class ApplicatorFizzBuzz extends FizzBuzz {
  private val applicators = Map(3 -> "Fizz", 5 -> "Buzz")

  def toFizzBuzz(n : Int) : String = {
    applicators.filterKeys(n % _ == 0).values.mkString
  }

  val fb = Stream.from(1).map(toFizzBuzz)
}

Mapを利用するとこんな書き方も出来る。条件分岐の部分がだいぶすっきりしている。拡張にも強く、

private val applicators = Map(3 -> "Fizz", 5 -> "Buzz", 7 -> "Woof")

とすれば FizzBuzzWoof になる。

(Main.scala は省略)

その 4 : toFizzBuzz を使わない

Haskell で同様のものが作られていて、それにヒントを得たもの。

package jp.mydns.akanekodou.scala.fizzbuzz

import jp.mydns.akanekodou.scala.collection.util.StreamUtils._

class StreamZipFizzBuzz extends FizzBuzz {
  private val f = List.fill(2)("") :+ "Fizz"
  private val b = List.fill(4)("") :+ "Buzz"

  val fb = (f.cycle zip b.cycle).map(t => t._1 + t._2)
}
package jp.mydns.akanekodou.scala.collection.util

object StreamUtils {
  implicit class iterableCycle [A] (self : Iterable[A]) {
    def cycle : Stream[A] = Stream.continually(self).flatten
  }
}

究極の禁じ手かも(苦笑)。暗黙クラスを利用してあたかも Iterable を継承するクラスのインスタンスcycle メソッドがあるかのように振る舞わせ、

"", "", "Fizz", "", "", "Fizz", "", "", "Fizz", "", "", "Fizz", "", "", "Fizz", ...

という Stream[String]

"", "", "", "", "Buzz", "", "", "", "", "Buzz", "", "", "", "", "Buzz", ...

という Stream[String] を作ってそれを zip すると

("", ""), ("", ""), ("Fizz", ""), ("", ""), ("", "Buzz"), ("Fizz", ""), ("", ""), ("", ""), ("Fizz", ""), ("", "Buzz"), ("", ""), ("Fizz", ""), ("", ""), ("", ""), ("Fizz", "Buzz"), ...

という Stream[(String, String)] が出来上がる。後は各 Tuple を文字列結合でつなげることで目的の Stream[String] を条件文を一切使わずに生成することに成功した。

(Main.scala は省略)

楽しい楽しい FizzBuzz の改造。Scala 編はとりあえず今回で一旦締めます。