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

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

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

前回のプログラムは無事動いたかな ? では改造を始める。でもその前に…。

SBT で作ったプロジェクトを Scala IDE for Eclipse で扱えるようにする

前回までの手順で、プロジェクト直下に project フォルダが出来ていると思う。そこに plugins.sbt というファイルを以下の内容で作成する。

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.5.0")

そうしたらプロジェクト直下で

> sbt eclipse

を実行する。これにより Eclipse 用の設定ファイルなどが作られ、Scala IDE for Eclipse でプロジェクトをインポートして扱うことが出来る。コーディング作業は IDE を使う方が楽なのでやっておくと良い。なお、SBT でライブラリの依存関係を変更したときは再度上記コマンドを実行しないと Eclipse 側に反映されないので注意。

最初の改造 : メソッド化する

まずはアルゴリズムの部分をメソッド化してみよう。

package jp.mydns.akanekodou.scala

object Main {
  def toFizzBuzz(n : Int) = {
    if (n % 3 == 0) {
      if (n % 5 == 0)
        "FizzBuzz"
      else
        "Fizz"
    } else if (n % 5 == 0) {
      "Buzz"
    } else {
      n
    }
  }

  def main(args : Array[String]) : Unit = {
    for (n <- 1 to 100) println(toFizzBuzz(n))
  }
}

Scalareturn 文を必要としない。これは幾分楽である。またメソッドの返り値の型を明記していない。これは返り値の型を型推論に任せることを意味する。今回の場合だと返り値に String 型と Int 型が混在しているので、型推論に任せてしまった方が記述は楽である。もちろん返り値の型がはっきりわかっている場合は、それを明記する書き方もある。

第二の改造 : 反復部分を書き換える

package jp.mydns.akanekodou.scala

object Main {
  def toFizzBuzz(n : Int) = {
    if (n % 3 == 0) {
      if (n % 5 == 0)
        "FizzBuzz"
      else
        "Fizz"
    } else if (n % 5 == 0) {
      "Buzz"
    } else {
      n
    }
  }

  def main(args : Array[String]) : Unit = {
    1 to 100 map toFizzBuzz foreach println
  }
}

これなどはだいぶ Scala らしい書き方になっている。map はコレクション(配列など)の全ての要素に指定した関数を適用するためのメソッドで、それに対して foreach を使って println で改行付き表示をさせている。オブジェクト指向に良くあるメソッドチェーンと関数型プログラミングの要素がここに含まれている。

第三の改造 : ファイルを分割する

新たに jp.mydns.akanekodou.scala.fizzbuzz パッケージを作成して次のような object を作る。

package jp.mydns.akanekodou.scala.fizzbuzz

object FizzBuzz {
  def toFizzBuzz(n : Int) = {
    if (n % 3 == 0) {
      if (n % 5 == 0)
        "FizzBuzz"
      else
        "Fizz"
    } else if (n % 5 == 0) {
      "Buzz"
    } else {
      n
    }
  }
}

そして Main.scala

package jp.mydns.akanekodou.scala

import jp.mydns.akanekodou.scala.fizzbuzz.FizzBuzz._

object Main {
  def main(args : Array[String]) : Unit = {
    1 to 100 map toFizzBuzz foreach println
  }
}

のように書き換える。object はシングルトンなので、そのメンバーをインポートするときは Scala では上記のような import 文の書き方になる。

第四の改造 : もうメソッド継承しちゃお ?

package jp.mydns.akanekodou.scala.fizzbuzz

class FizzBuzz {
  def toFizzBuzz(n : Int) = {
    if (n % 3 == 0) {
      if (n % 5 == 0)
        "FizzBuzz"
      else
        "Fizz"
    } else if (n % 5 == 0) {
      "Buzz"
    } else {
      n
    }
  }
}
package jp.mydns.akanekodou.scala

import jp.mydns.akanekodou.scala.fizzbuzz.FizzBuzz

object Main extends FizzBuzz {
  def main(args : Array[String]) : Unit = {
    1 to 100 map toFizzBuzz foreach println
  }
}

class として定義すると継承が出来るようになる。toFizzBuzzMain のメンバーとして使うことが出来るようになった。

第五の改造 : main 書くの('A`)マンドクセ

package jp.mydns.akanekodou.scala

import jp.mydns.akanekodou.scala.fizzbuzz.FizzBuzz

object Main extends FizzBuzz with App {
  1 to 100 map toFizzBuzz foreach println
}

Scala には App というトレイトが用意されていて、これをミックスインすると main をいちいち書かなくても良くなる。

さて、ここまでの改造では反復に関する記述に多少手を加えたが、もう一つの肝である条件分岐に関してはまだ手を加えていない。次回から徐々に条件分岐の部分にも手を加えていくことにする。