Rubyのcaseを使ってFizzBuzzを書く
Shinosaka.rb #4におじゃましてきました。
今日はFizzBuzzを書くという課題でした。
FizzBuzzは何回か書いたことがあったので、あたらしい書き方ができないか考えてみたんですが、case式を使えば今までにないFizzBuzzを書けるんじゃないかと思ったんですが、あまりうまくいきませんでした。
それがこのコードなんですが、イメージしてたのはこういうのではなくて、caseのところになんか判定するやつを書いておいて、when のところには15, 3, 5 だけ書いておけばいいようにしたかった。
そういうのをしたかったんですが、Rubyのドキュメント読みなおしてみると、whenの引数をレシーバとして === を実行し、caseの引数を右辺値として実行した結果を評価するとのことで、caseのところにlambdaを置いておくというのではうまくいかなそうなことがわかりました。caseのパラメータをレシーバにしてくれれば良かったんですが...
それで他に手は無いかなと考えてみました。
Fixnum#===
をオーバーライドした解です。
case式は非常にすっきりしていい感じです。Refinementsを使っているので、Integer#===の挙動が変わるのはこのファイルだけということも保証されてます。
でもちょっと大げさな感じなので、できればlambdaを使ってなんとかしたい。
そこでこんなふうに実装してみました。
なかなかいいですね。caseにlambda渡すのは諦めて、whenにlambdaを渡すようにしました。 でも個人的に、caseの引数に何も渡してないのが不満。lambdaにiを閉じ込めてるからいいといえばいいんですが、それならifで良くない?という気がしてしまいます。
そこで、curry
メソッドを使って以下のように書き換えてみました。
caseにiを渡して評価するようにしました。Proc#=== は Proc#call と同じメソッドなので、
matcher.curry[15][i]
のように実行されているわけですね。 でも今度はcurryと書いてるのがうっとおしくなってきました。curryを使うんじゃなくて、lambdaを返すlambdaを定義してみましょうか。
whenの後ろがスッキリしました。その分lambdaの定義が読みにくくなってしまいましたが...まあいいでしょう。満足しました。
新しいFizzBuzzの書き方を思いつけて良かったです。ありがとうございました。
(2014/5/1追記)
curryの使い方を勘違いしてました。curryは引数を一つづつとるlambdaを返すんだから、予めcurryしておけばスッキリ書けた。
これでOKですね。curryを使うと、上に書いた、入れ子になってるlambdaと同等のlambdaを出力してくれる。