2007-09-28
■ クラスに対して動的にメソッドを追加する練習

#song.rb class Song attr_accessor :name, :artist def initialize(name, artist) @name = name @artist = artist end end
Songクラスにshow_infoというインスタンスメソッドを動的に追加してみます。
irb(main):001:0> load 'song.rb' => true // Songオブジェクトを二つ生成します。 irb(main):002:0> s1 = Song.new("桜の時", "aiko") => #<Song:0x6c480 @name="桜の時", @artist="aiko"> irb(main):003:0> s2 = Song.new("Hello Another Way -それぞれの場所-", "the brilliant green") => #<Song:0x5e5b0 @name="Hello Another Way -それぞれの場所-", @artist="the brilliant green"> // Songクラスにshow_infoメソッドがないのでエラーになります。 irb(main):004:0> s1.show_info NoMethodError: undefined method `show_info' for #<Song:0x6c480 @name="桜の時", @artist="aiko"> from (irb):5 // s1同様にエラーになります。 irb(main):005:0> s2.show_info NoMethodError: undefined method `show_info' for #<Song:0x5e5b0> from (irb):6 // Songクラスに対して動的にshow_infoメソッドを追加します。 irb(main):006:0> Song.class_eval do irb(main):007:1* def show_info irb(main):008:2> return "Title: #{self.name} -- Artist: #{self.artist}" irb(main):009:2> end irb(main):010:1> end => nil // Songクラスにshow_infoメソッドを追加したので、show_infoメソッドを呼び出してもエラーになりません。 irb(main):011:0> s1.show_info => "Title: 桜の時 -- Artist: aiko" irb(main):012:0> s2.show_info => "Title: Hello Another Way -それぞれの場所- -- Artist: the brilliant green"
// s1とs2のshow_infoの振る舞いを変えてみます。 irb(main):016:0> def s1.show_info irb(main):017:1> return "#{self.name} / #{self.artist}" irb(main):018:1> end s1がshow_infoメソッドを呼び出したときは曲名とアーティスト名がシンプルに表示される。 irb(main):019:0> s1.show_info => "桜の時 / aiko" // s2オブジェクトに対してshow_infoメソッドを再定義 irb(main):020:0> def s2.show_info irb(main):021:1> return "曲名: #{self.name} アーティスト名: #{self.artist}" irb(main):022:1> end => nil s2がshow_infoを呼び出した時はs1と異なる表示のされかたをしている。 irb(main):023:0> s2.show_info => "曲名: Hello Another Way -それぞれの場所- アーティスト名: the brilliant green"
2007-07-03
■ 差集合

Rubyの場合、集合A-集合Bと書くことによって簡単に差集合を求めることが出来ます。
サンプルコード
# # difference_set.rb # langs = ["Python", "C", "Ruby", "Java", "Perl", "PHP", "C#" ,"C++"] not_light_weight_langs = ["C", "Java", "C#", "C++"] #langsとnot_light_weight_langsの差集合を求めている。 lightWeight_langs = langs - not_light_weight_langs #上で求めた差集合の結果を、シングルクォートでjoinして、",PHP$"にマッチするものを" and PHP"で置換した結果を標準出力 puts "#{lightWeight_langs.join(',').sub(/,PHP/, ' and PHP')} are called LightWeight Language."
実行結果
Python,Ruby,Perl and PHP are called LightWeight Language.
Frenchy2011/09/09 02:36Thank you so much for this atricle, it saved me time!
rwucpnv2011/09/09 16:50n6tQMb <a href="http://lplfdgfnmkrg.com/">lplfdgfnmkrg</a>
qpqsae2011/09/10 17:581w5scn , [url=http://djynmnqanisd.com/]djynmnqanisd[/url], [link=http://bclondtnvkay.com/]bclondtnvkay[/link], http://tyikvadicvwr.com/
rcjgkknps2011/09/11 23:12cgea7A <a href="http://ihyyfofljtae.com/">ihyyfofljtae</a>
nvcuqp2011/09/12 21:02yE44Fg , [url=http://rwbcpraupbsi.com/]rwbcpraupbsi[/url], [link=http://rrxtyycgzdvc.com/]rrxtyycgzdvc[/link], http://dekkmlhosbmq.com/
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のドライバ読み込み及びコネクション取得のコードとあまり変わりはないのかな(?)
JoyceOkay I'm convinced. Let's put it to aitcon.
bkixldibotp7PJ9qE <a href="http://dgwfqdyyqqqm.com/">dgwfqdyyqqqm</a>
bgfqywrac50ykLW , [url=http://pxyavitoqudk.com/]pxyavitoqudk[/url], [link=http://sbysmbdavknc.com/]sbysmbdavknc[/link], http://bfpqukafzeud.com/
zbzbeawjqp537XoC <a href="http://gtcdcctqzqpu.com/">gtcdcctqzqpu</a>
zdqgxulDGx0PY , [url=http://synexmpphtlm.com/]synexmpphtlm[/url], [link=http://mfqvmontfsjy.com/]mfqvmontfsjy[/link], http://vhwwuaheevtl.com/
2007-06-30
■ 多重代入

多重代入は便利ですね。
irb(main):001:0> langs = ["Perl", "PHP", "Python", "Ruby"] => ["Perl", "PHP", "Python", "Ruby"] irb(main):002:0> puts langs Perl PHP Python Ruby => nil #多重代入を使用し、変数a,b,c,dのそれぞれに配列langsの第一要素から順に値を割り当てている。 irb(main):003:0> a, b, c, d = langs => ["Perl", "PHP", "Python", "Ruby"] irb(main):004:0> puts a Perl => nil irb(main):005:0> puts b PHP => nil irb(main):006:0> puts c Python => nil irb(main):007:0> puts d Ruby => nil irb(main):008:0> "a -> #{a} b -> #{b} c -> #{c} d -> #{d}" => "a -> Perl b -> PHP c -> Python d -> Ruby"