2007-07-01
■ 99 Bottles of Beer

Code Golfに掲載されていた99 Bottles Of Beer問題をRubyを使い、尚かつテストファーストで書いてみました。
まだリファクタリング出来るところが残っていると思いますが、一旦載せておいて、リファクタリング前後の変化を自分へのメモとして残しておきたいと思います。
ファイルの配置場所
|-- lib
| `-- ninety_nine_bottle_of_beer.rb
`-- test
`-- ninty_nine_bottle_of_beer_test.rb
リファクタリング前
#ninty_nine_bottle_of_beer_test.rb $:.unshift File.join(File.dirname(__FILE__), "..", "lib") require 'test/unit' require 'ninety_nine_bottle_of_beer' class NinetyNineBottlesOfBeerTest < Test::Unit::TestCase def setup @target = NinetyNineBottlesOfBeer.new end def test_sing result = @target.sing expected_lyrics_at_index_zero = create_lyrics_for(99) expected_lyrics_at_index_ninety_six = create_lyrics_for(3) expected_lyrics_at_index_ninety_seven = create_lyrics_for(2) expected_lyrics_at_index_ninety_eight = "1 bottle of beer on the wall, 1 bottle of beer.\n" expected_lyrics_at_index_ninety_eight += "Go to the store and buy some more, 99 bottles of beer on the wall.\n" assert_equal(expected_lyrics_at_index_zero , result[0]) assert_equal(expected_lyrics_at_index_ninety_six , result[96]) assert_equal(expected_lyrics_at_index_ninety_seven , result[97]) assert_equal(expected_lyrics_at_index_ninety_eight , result[98]) assert_equal(99, result.size) end private def create_lyrics_for(i) result = "#{i} bottles of beer on the wall, #{i} bottles of beer.\n" if i == 2 result += "Take one down and pass it around, 1 bottle of beer on the wall.\n" else result += "Take one down and pass it around, #{i - 1} bottles of beer on the wall.\n" end end end
- プロダクトコード
#ninety_nine_bottle_of_beer.rb class NinetyNineBottlesOfBeer def sing result = [] 99.downto(1) do |count| if (count == 1) lyrics = "1 bottle of beer on the wall, 1 bottle of beer.\n" lyrics += "Go to the store and buy some more, 99 bottles of beer on the wall.\n" else lyrics = "#{count} bottles of beer on the wall, #{count} bottles of beer.\n" lyrics += "Take one down and pass it around, " if count == 2 lyrics += "1 bottle of beer on the wall.\n" else lyrics += "#{count - 1} bottles of beer on the wall.\n" end end result << lyrics end return result end end str = NinetyNineBottlesOfBeer.new.sing File.open("99bottle.txt","w") do |file| file << str end
リファクタリング後
テストコードはプロダクトコードのメソッド名を変更したので、それに伴いテストコード内のメソッド名を変更しました。変更はそれだけです。
$:.unshift File.join(File.dirname(__FILE__), "..", "lib") require 'test/unit' require 'ninety_nine_bottle_of_beer' class NinetyNineBottlesOfBeerTest < Test::Unit::TestCase def setup @target = NinetyNineBottlesOfBeer.new end def test_make_lyrics result = @target.make_lyrics expected_lyrics_at_index_zero = create_lyrics_for(99) expected_lyrics_at_index_ninety_six = create_lyrics_for(3) expected_lyrics_at_index_ninety_seven = create_lyrics_for(2) expected_lyrics_at_index_ninety_eight = "1 bottle of beer on the wall, 1 bottle of beer.\n" expected_lyrics_at_index_ninety_eight += "Go to the store and buy some more, 99 bottles of beer on the wall.\n" assert_equal(expected_lyrics_at_index_zero , result[0]) assert_equal(expected_lyrics_at_index_ninety_six , result[96]) assert_equal(expected_lyrics_at_index_ninety_seven , result[97]) assert_equal(expected_lyrics_at_index_ninety_eight , result[98]) assert_equal(99, result.size) end private def create_lyrics_for(i) result = "#{i} bottles of beer on the wall, #{i} bottles of beer.\n" if i == 2 result += "Take one down and pass it around, 1 bottle of beer on the wall.\n" else result += "Take one down and pass it around, #{i - 1} bottles of beer on the wall.\n" end end end
class NinetyNineBottlesOfBeer def make_lyrics result = [] 99.downto(1) do |i| lyrics = "#{i} bottle#{"s" if i > 1} of beer on the wall, #{i} bottle#{"s" if i > 1} of beer.\n" if (i == 1) lyrics += "Go to the store and buy some more, 99 bottles of beer on the wall.\n" else lyrics += "Take one down and pass it around, #{i - 1} bottle#{"s" if i > 2} of beer on the wall.\n" end result << lyrics end return result end end lyrics = NinetyNineBottlesOfBeer.new.make_lyrics File.open("99bottle.txt","w") do |file| file << lyrics end
感想
もっと短く出来るのかなあと考えてみましたが、良い案が浮かびませんでした(^^;
そもそもクラスを作る必要かあるのかも疑問なところです。
今回NinetyNineBottlesOfBeerというクラスを作りましたがクラス名がしっくりきません。
またPoetというクラスを作成しsingメソッドを実装して生成したNinetyNineBottlesOfBeerの詩を渡してPoetに99bottles of beerを歌ってもらうのも面白いかなと思いましたが、そこまで複雑にする必要はないなと思いやめました。
因に99 Bottles of Beerというサイトでは1103種類の異なる言語やバリエーションで99 Bottles of Beerのコードを紹介しています。
■ クラスロード時に行ないたい処理を書く その1

class Hoge #クラスがロードされた時に以下の処理が実行される %w(Perl Python PHP Ruby).each {|lang| puts lang} end
class中に上記のような処理を書いておくと、クラスがロードされたときに実行されます。JavaのStatic Initializerと同じと考えて良いのかも(?)
実行例
#クラスをロード irb(main):001:0> load 'hoge.rb' #クラスをロードしたので、%w(Perl Python PHP Ruby).each {|lang| puts lang}の処理が実行され、配列の各要素が標準出力されました。 Perl Python PHP Ruby
■ クラスロード時に行ないたい処理を書く その2

ファイル構成
|-- db_util.rb `-- jdbc.properties
jdbc.propertiesの内容
jdbc.driver=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb:hsql://localhost jdbc.user=sa jdbc.password=
Javaのプロパティファイルjdbc.propertiesの内容を読み取り、プロパティファイルの各キーに対する値をクラス変数に設定するサンプルプログラムを書いてみました。
#実用性はほとんどなし。
多重代入とクラスロード時に行ないたい処理(Static Initializer)の練習です。
class DBUtil def self.get_db_config_properies return [] << @@driver << @@url << @@user << @@password end private def self.init #jdbc.propertieswを開いて、全行を読み取ります。また読み取った各行は配列の要素になります。 File.open("jdbc.properties", "r") do |file| @@list = file.readlines end #配列@@listの各要素からjdbc.xxxxx=にマッチする文字列を空文字で置換 @@list.map! {|x| x.gsub(/jdbc.\w+=/, '').chomp} #配列@@listの各要素を以下のクラス変数に代入 @@driver, @@url, @@user, @@password = @@list end #クラスロード時に以下の処理が実行されます。 self.init end
実行例
#ロード時にjdbc.propertiesの読み取りと余分な文字列の排除及び読み取った各行を配列に代入しています。 irb(main):001:0> load 'db_util.rb' => true irb(main):002:0> DBUtil.get_db_config_properies #jdbc.propertiesの各キーにたいするそれぞれの値がリストに入って返ってきました。 => ["org.hsqldb.jdbcDriver", "jdbc:hsqldb:hsql://localhost", "sa", ""]
感想
JRubyを使用して、JDBCを使ってDBに接続する処理を書くときに、上記のDBUtilクラスを使ってみたいなと思い書いてみました。しかしドライバーが無かった場合やコネクションが取得できなかった場合の例外処理を書いていくとJavaのドライバ読み込み及びコネクション取得のコードとあまり変わりはないのかな(?)
Joyce2011/09/08 11:56Okay I'm convinced. Let's put it to aitcon.
bkixldibotp2011/09/09 00:267PJ9qE <a href="http://dgwfqdyyqqqm.com/">dgwfqdyyqqqm</a>
bgfqywrac2011/09/09 20:4950ykLW , [url=http://pxyavitoqudk.com/]pxyavitoqudk[/url], [link=http://sbysmbdavknc.com/]sbysmbdavknc[/link], http://bfpqukafzeud.com/
zbzbeawjqp2011/09/10 18:26537XoC <a href="http://gtcdcctqzqpu.com/">gtcdcctqzqpu</a>
zdqgxul2011/09/12 19:14DGx0PY , [url=http://synexmpphtlm.com/]synexmpphtlm[/url], [link=http://mfqvmontfsjy.com/]mfqvmontfsjy[/link], http://vhwwuaheevtl.com/