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

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

2007-09-28

クラスに対して動的にメソッドを追加する練習  クラスに対して動的にメソッドを追加する練習 - のびのびなRuby日記 を含むブックマーク はてなブックマーク -  クラスに対して動的にメソッドを追加する練習 - のびのびなRuby日記  クラスに対して動的にメソッドを追加する練習 - のびのびなRuby日記 のブックマークコメント

#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"

Rubyオブジェクト毎にメソッドを定義て、これを特異メソッドと言います。

2007-09-14

ConsoleでもUnitTestのグリーンバーを表示 ConsoleでもUnitTestのグリーンバーを表示 - のびのびなRuby日記 を含むブックマーク はてなブックマーク - ConsoleでもUnitTestのグリーンバーを表示 - のびのびなRuby日記 ConsoleでもUnitTestのグリーンバーを表示 - のびのびなRuby日記 のブックマークコメント

Term::ANSIColor for Rubyを見てTerm::ANSIColor for Rubyインストールしてみました。

2007-07-03

差集合  差集合 - のびのびなRuby日記 を含むブックマーク はてなブックマーク -  差集合 - のびのびなRuby日記  差集合 - のびのびなRuby日記 のブックマークコメント

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.

FrenchyFrenchy2011/09/09 02:36Thank you so much for this atricle, it saved me time!

rwucpnvrwucpnv2011/09/09 16:50n6tQMb <a href="http://lplfdgfnmkrg.com/">lplfdgfnmkrg</a>

qpqsaeqpqsae2011/09/10 17:581w5scn , [url=http://djynmnqanisd.com/]djynmnqanisd[/url], [link=http://bclondtnvkay.com/]bclondtnvkay[/link], http://tyikvadicvwr.com/

rcjgkknpsrcjgkknps2011/09/11 23:12cgea7A <a href="http://ihyyfofljtae.com/">ihyyfofljtae</a>

nvcuqpnvcuqp2011/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  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/

2007-06-30

多重代入 多重代入 - のびのびなRuby日記 を含むブックマーク はてなブックマーク - 多重代入 - のびのびなRuby日記 多重代入 - のびのびなRuby日記 のブックマークコメント

多重代入は便利ですね。

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"