#はじめに
この記事はFizzBuzz Advent Calendar 2017の 12/24 に向けて書いたものです。
もちろん、FizzBuzz Advent Calender に Ruby版が無い!!
ということで書きました。
#Rubyとは
Rubyとは...
オープンソースの動的なプログラミング言語で、 シンプルさと高い生産性を備えています。 エレガントな文法を持ち、自然に読み書きができます。(公式サイトより)
#FizzBuzz
プログラミング能力をみるテストに使われるFizzBuzzは以下のように使われます。
1から順番に数値を出力します。ただし、下記の条件をみたすとき、数値の代わりに指定した文字列を出力します。
- 3で割り切れるとき "Fizz"
- 5で割り切れるとき "Buzz"
- 3と5で割り切れるとき "FizzBuzz"
簡単なんだけど、焦っていると間違えてしまいがちですね〜
#実装
##要求仕様
1 から n までの数値について FizzBuzz して、 fn に書き込むメソッドを作成します。
fizzbuzz(n, fn)
##テスト
まずはテストを書きましょう。
1 から 100 までの期待する答えを spec/fizz_buzz.answer に作ります。その後、rspec ファイルを作成します。
# spec/fizz_buzz_spec.rb
require_relative '../fizz_buzz.rb'
describe :FizzBuzz do
before :all do
dir = File.expand_path(File.dirname(__FILE__))
@_read = ->(fn){ File.open(fn).map{ |r| r.chomp }}
@answer = @_read[File.join(dir, 'fizz_buzz.answer')]
end
it 'should be eauql to anser' do
fn = 'fizz_buzz.result'
fizzbuzz(100, fn)
expect(@_read[fn]).to match_array @answer
end
end
下記コマンドでテストします。
$ rspec spec
##最初の実装
最初の実装なので、とりあえず動かします。
def fizzbuzz(n, fn)
File.open(fn,'w') do |f|
1.upto(n) do |i|
out = ''
i % 3 == 0 and out << 'Fizz'
i % 5 == 0 and out << 'Buzz'
f.puts out.empty?? i : out
end
end
end
##文字列演算を避ける
文字列演算は重いので、避けてみました。
def fizzbuzz(n, fn)
File.open(fn,'w') do |f|
1.upto(n) do |i|
f.puts case
when i % 15 == 0 then 'FizzBuzz'
when i % 3 == 0 then 'Fizz'
when i % 5 == 0 then 'Buzz'
else i
end
end
end
end
##ハッシュを使う
割り算が3つもあるので、なんとかしたいです。
ということで、ハッシュを使ってみます。
class FizzBuzz
CONDITION = { 0 => 'FizzBuzz' }
{ 3 => 'Fizz',
5 => 'Buzz',
}.each do |no, key|
no.step(14,no){ |i| CONDITION[i] = key }
end
class << self
public
def fizzbuzz(n, fn)
File.open(fn,'w') do |f|
1.upto(n) do |i|
f.puts CONDITION[i % 15] || i
end
end
end
end
end
# wrapper
def fizzbuzz(n, fn)
FizzBuzz.fizzbuzz(n, fn)
end
wrapper は要求仕様に従うためのものですが、変更が可能であればその方が良いですね。
##ベンチマーク
せっかく作ったので、ベンチマークを取ってみます。
メソッド名を適当に変えます。あと、wrapper も外します。
require 'benchmark'
n = 10000000
fn = 'fizzbuzz.out'
Benchmark.bm do |r|
r.report('no 1'){ fizzbuzz1(n, fn) }; File.delete(fn)
r.report('no 2'){ fizzbuzz2(n, fn) }; File.delete(fn)
r.report('no 3'){ FizzBuzz.fizzbuzz(n, fn) }; File.delete(fn)
end
結果は以下の通りでした。
user system total real
no 1 5.810000 0.160000 5.970000 ( 6.045857)
no 2 5.120000 0.150000 5.270000 ( 5.311798)
no 3 4.850000 0.130000 4.980000 ( 4.990330)
目論見どおり、早くなりました。
よかった...
#おわりに
Testドリブンな開発を FizzBuzz を例にして行った形になりました。
Ruby が好きになってくれると嬉しいです。