トップ 最新の日記 ユーザー登録 ログイン ヘルプ

のびのびなRuby日記 このページをアンテナに追加 RSSフィード

2007-07-01

99 Bottles of Beer  99 Bottles of Beer - のびのびなRuby日記 を含むブックマーク はてなブックマーク -  99 Bottles of Beer - のびのびなRuby日記  99 Bottles of Beer - のびのびなRuby日記 のブックマークコメント

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 クラスロード時に行ないたい処理を書く その1 - のびのびなRuby日記 を含むブックマーク はてなブックマーク - クラスロード時に行ないたい処理を書く その1 - のびのびなRuby日記 クラスロード時に行ないたい処理を書く その1 - のびのびなRuby日記 のブックマークコメント

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 クラスロード時に行ないたい処理を書く その2 - のびのびなRuby日記 を含むブックマーク はてなブックマーク - クラスロード時に行ないたい処理を書く その2 - のびのびなRuby日記 クラスロード時に行ないたい処理を書く その2 - のびのびなRuby日記 のブックマークコメント

ファイル構成

|-- 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ドライバ読み込み及びコネクション取得のコードとあまり変わりはないのかな(?)

JoyceJoyce2011/09/08 11:56Okay I'm convinced. Let's put it to aitcon.

bkixldibotpbkixldibotp2011/09/09 00:267PJ9qE <a href="http://dgwfqdyyqqqm.com/">dgwfqdyyqqqm</a>

bgfqywracbgfqywrac2011/09/09 20:4950ykLW , [url=http://pxyavitoqudk.com/]pxyavitoqudk[/url], [link=http://sbysmbdavknc.com/]sbysmbdavknc[/link], http://bfpqukafzeud.com/

zbzbeawjqpzbzbeawjqp2011/09/10 18:26537XoC <a href="http://gtcdcctqzqpu.com/">gtcdcctqzqpu</a>

zdqgxulzdqgxul2011/09/12 19:14DGx0PY , [url=http://synexmpphtlm.com/]synexmpphtlm[/url], [link=http://mfqvmontfsjy.com/]mfqvmontfsjy[/link], http://vhwwuaheevtl.com/