Ruby で FizzBuzz を満喫する
Scala にだいぶSAN値を削られたのでここいらで Ruby に日和ろう(苦笑)。
その 1 : Object#tap
を利用する
to_fizzbuzz = -> n { "".tap do |_| _ << "Fizz" if n % 3 == 0 _ << "Buzz" if n % 5 == 0 break n if _.empty? end } puts 1.upto(100).map(&to_fizzbuzz)
Ruby の Object#tap
はブロック内で自分自身に対して行われた破壊的変更が保証される。break
が発動するとそれらの変更を破棄して強制的に別の値を返すことが出来る。発動しなければブロック内で行われた破壊的変更を適用した自身が返ることになる。
さくっと lambda 奴を使っておくと Enumerable#map
に Proc
オブジェクトを渡す Ruby らしい書き方で処理が記述できる。
その 2 : Hash
を利用する
Scala 編で Map
を使ったやつの元ネタ。
Applicators = {3 => :Fizz, 5 => :Buzz} to_fizzbuzz = -> n { fb = Applicators.select{|_| n % _ == 0}.values.join fb.empty? ? n : fb } puts 1.upto(100).map(&to_fizzbuzz)
やってることはほぼ一緒。メソッド名が違うだけ。
その 3 : Enumerable#zip
を利用する
fizz = [nil] * 2 << :Fizz buzz = [nil] * 4 << :Buzz 1.upto(100).zip(fizz.cycle, buzz.cycle) do |n, *f| puts f.any? ? f.join : n end
ネットで見つけてきた。nil
と any
の使い方が Ruby っぽくてお気に入りである。
Scala でとことん FizzBuzz する(その 4)
最初にお詫び。前回の記事にかなり大幅な修正を加えています。再度確認をしていただけるとありがたいです。
追記 : 今回作ったソースファイル群(+ テストコード)を GitHub で公開しました。
akaneko3/FizzBuzzScala · GitHub
Scala でとことん FizzBuzz する(その 3)
さぁ、改造を続けよう。
FizzBuzz アルゴリズム再考
ちょっとひねって、次のような写像を考える。
何も書いていないところは空文字列を対応させていると考える。これを利用して、FizzBuzz のアルゴリズムを少し変更する。
- 何らかの方法で
"", "", "Fizz", "", "Buzz", "Fizz", "", "", "Fizz", "Buzz", "", "Fizz", "", "", "FizzBuzz", ...
と繰り返される列を用意する。 - 1 から順に上記の列と対応させていく(
zip
) - 上記で得られた列を順に処理していくとき、「数字と空文字列」の組み合わせのときは数字を、「数字と空でない文字列」の組み合わせのときは文字列を返すようにする。
これを Scala のトレイトを利用して抽象クラスとしてあらかじめ作っておく。
package jp.mydns.akanekodou.scala.fizzbuzz abstract class FizzBuzz { protected val fb : Stream[String] def fizzbuzz : Unit = { 1 to 100 zip fb map { case (n, "") => n case (_, s) => s } foreach println } }
Java のインターフェースと違い、Scala のトレイトにはこうした共通処理をあらかじめ実装しておくことが出来る。今回、ついでなので表示するところまでまとめて実装してみた。
2015/04/12 追記 : 今回のケースではトレイトを使う必要はありませんでした。お詫びの上、通常の抽象クラスに訂正させていただきます。
もう一つ、Scala の機能である「暗黙引数」を利用している。一見するとメソッドの引数のデフォルト値をあらかじめ与えるのと似ているが、あちらはあらかじめ引数を初期化しておかないとダメなのに対し、こちらはメソッド呼び出し時までに暗黙引数を初期化・確定させておけば良い。
Java の抽象クラスは「抽象メソッドが含まれるクラス」であるが、Scala の場合は抽象メソッドに加えて上記のような抽象フィールドを含むクラスも抽象クラスとして定義しなければならない。
2015/04/12 追記 2 : Main
に FizzBuzz 系クラスを継承させる方法が何となく行儀が悪く見えたので止めました。
その他のファイルも適宜書き換えていこう。
package jp.mydns.akanekodou.scala.fizzbuzz class FirstFizzBuzz extends FizzBuzz { def toFizzBuzz(n : Int) : String = { if (n % 3 == 0) { if (n % 5 == 0) "FizzBuzz" else "Fizz" } else if (n % 5 == 0) { "Buzz" } else { "" } } val fb = Stream.from(1).map(toFizzBuzz) }
toFizzBuzz
は最早 String
型しか返さないことが明白になった。
package jp.mydns.akanekodou.scala import jp.mydns.akanekodou.scala.fizzbuzz._ object Main extends App { (new FirstFizzBuzz).fizzbuzz }
Main.scala
がだいぶすっきりした。
いよいよ本格的に改造していくぞ !
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 側に反映されないので注意。
Scala でとことん FizzBuzz する(その 1)
プログラミングをこよなく愛する皆さん、今日も元気に FizzBuzz してますか !
「FizzBuzz って何 ?」という人から「FizzBuzz が何の役に立つんだよ」という人まで、今回は存分に FizzBuzz を楽しめる(?)連載を始めますよ !
「FizzBuzz」とは何か
FizzBuzz。それはもっぱら英語圏で行われるちょっとしたレクリエーションのようなものである。数人が輪になって、最初の人から順々に数字を 1 から順に 100 まで言っていくのであるが、
- 数字が 3 の倍数(3, 6, ...)のときは数字を言う代わりに「Fizz」と言う
- 数字が 5 の倍数(5, 10, ..)のときは数字を言う代わりに「Buzz」と言う
- 上記の両方に当てはまるとき、つまり数字が 15 の倍数(15, 30, ...)のときは数字を言ったり「Fizz」や「Buzz」と言う代わりに「FizzBuzz」と言う
- それ以外のときは普通に数字を言う
という遊びである。これをプログラミングでやるときは、1 から 100 まで出力させ、その際に数字が 3 の倍数だったり 5 の倍数だったりするときにその出力を上記の文字列で置き換えて表示させるプログラムを組むことを FizzBuzz と言っている。
何故 FizzBuzz なのか
何故 FizzBuzz をプログラミングすることがそんなに大事なのかというと
- 入出力
- 反復
- 条件分岐
の要素に習熟することに大変優れているから、というのが通説のようである。とりわけ反復と条件分岐が強調されることが多い。入出力は、いわゆる標準入出力はもちろんだが、広く「関数の in と out の関係」についても学ぶことが出来る。
原始 FizzBuzz
今回は Scala による FizzBuzz の様々な実装を考えてみることにする。まず最も素朴な FizzBuzz はこうなるだろう。
package jp.mydns.akanekodou.scala object Main { def main(args : Array[String]) : Unit = { for (n <- 1 to 100) { if (n % 3 == 0) { if (n % 5 == 0) println("FizzBuzz") else println("Fizz") } else if (n % 5 == 0) { println("Buzz") } else { println(n) } } } }
我々はこのソースコードからスタートして、さまざまな改造をしていくことになる。
SBT(Simple Build Tool)の導入
Scala プログラミングにおけるビルドツールの定番とされているのが SBT である。今回はこいつを使ってプロジェクトを作り、管理していく。Windows であれば MSI 形式のインストーラが配布されているのでインストールは難しくない。
プロジェクトの作成
まずプロジェクトのルートとなるディレクトリを作る。その中に build.sbt
というファイル名で SBT 用の設定ファイルを作る。中身はこんな感じ。
name := "FizzBuzz" version := "1.0" scalaVersion := "2.11.6"
空行は省略できない。次にソースファイルを配置するフォルダの作成。プロジェクトルートで
> mkdir src\main\scala\jp\mydns\akanekodou\scala
作ったフォルダに先ほどのソースコードを Main.scala
というファイル名で保存し配置する。
プロジェクトのビルドと実行
プロジェクトルートで
> sbt
を実行する。初回起動時は必要なライブラリをダウンロードしてくるため時間が掛かる。気長に待とう。プロンプトが帰ってきたら
> compile > run
でコンパイル→実行できる。さて、意図した通りにプログラムは動いたかな ?
エラトステネスの篩はもう古い !?
タイトルは親父ギャグじゃねーです !
ゆっちさんがこんな記事を書いていたようで。
ゆっちのBlog » Java8 時代の素数の求め方
最初はそのままパクらせていただいて動かしていました(それでもなかなかに早い)が、地味に無駄な処理があることに気がつきました。
boxed
して collect
してる部分がそれです。IntStream
のままでも count
メソッドで個数は確認できるので、以下のように修正してみました。あと、秒未満の処理時間は細かい数値はいらないと思ったのでミリ秒単位で切って表示させてます。
Stream API を用いた並列化された素数検出プログラム
実行時に他のプロセスが動いてたりするとアレですが、一応の目安として
1973815 個の素数を検出しました。 0 時間 0 分 32 秒 914
と、期待される結果と共に、家の非力なマシンでも 30 秒そこそこで処理が完了する*1ことがわかりました。Java8 すげぇ。
*1:オリジナル版だと 1 分ちょい掛かる