Ruby 1.8.7ではRuby 1.9からのbackportがとても多い。つまり、Ruby 1.9のあのメソッドがRuby 1.8でも使えるようになったということだ！！

これがすごいという機能がもりだくさん、ちょっと大人になったRuby 1.8をお楽しみに。

Enumeratorは組み込みになり、eachなどのイテレータメソッドはブロックをつけないとEnumerable::Enumeratorを返すようになった。おかげでブロック付きメソッドの柔軟性が飛躍的にアップ！

expectationsテスティングフレームワークによるテストで書いているので「gem install expectations」してから実行してみよう。手軽にユニットテストが書けるからおすすめ。書式は…見ればわかるよねｗ

ChangeLogで現在からRuby 1.8.6リリースまでを読んだので、ほとんどカバーしていると思われる。つかれた…

#!/usr/local/bin/ruby -wKe # -*- coding: euc-jp -*- # update (find-memofile "hatena/2008-05-08.txt") require 'rubygems' require 'expectations' # `gem install expectations' # Wed May 7 08:46:44 2008 Yukihiro Matsumoto <matz@ruby-lang.org> まで Expectations do # おいおい、見ろよ。Ruby 1.8.7だとブロックにブロックを渡せるんだぜ！ expect "block is passed" do mod = Module.new do define_method(:foo) do |&block| block.call end end extend(mod).foo { "block is passed" } # !> method redefined; discarding old expects end # Symbol#to_procってすげえええー！ expect [3, 3, 3, 5] do %w[foo bar baz fobar].map(&:length) end expect [1, 3] do [1,2,3,4].select(&:odd?) end # Ruby 1.9最強の萌えメソッドObject#tapがついに使えるようになったぜ！ expect(:two=>2, :one=>1) do {}.tap{|h| h[:one]=1; h[:two]=2 } end # Object#instance_execキターーーーーー（゜∀゜）ーーーーーー！！ expect 3 do "foo".instance_exec do length end end expect "foobar" do "foo".instance_exec("bar") do |x| self + x end end expect "FOO" do mod = Module.new do def def_each(*methods, &block) methods.each do |meth| define_method(meth) do instance_exec(meth, &block) end end end end klass = Class.new do extend mod def_each :foo, :bar do |meth| meth.to_s.upcase end end klass.new.foo end expect [5, 7] do a = 5 b = 7 instance_exec(a, b) do |x, y| [x, y] end end # Module#module_exec (class_exec) もよろしく。 # instance_execがinstance_evalの進化形に対して、module_execはmodule_evalの進化形。 expect 8 do klass = Class.new klass.module_exec(7) do |v| define_method(:hoge) { v+1 } end klass.new.hoge end # Binding#evalはeval(expr, binding)と等価。 expect :in_block do bind = Object.new.instance_eval do var = :in_block binding end bind.eval("var") end # __method__はメソッド名を返す疑似変数 expect :meth do mod = Module.new do def meth __method__ end end extend(mod).meth end # MatchData#inspectがわかりやすくなった。これで正規表現のデバッグも楽になるよね。 expect '#<MatchData "abc" 1:"a" 2:"b" 3:"c">' do "abc".match( /(.)(.)(.)/ ).inspect end # Method#receiverはレシーバ expect "recv" do "recv".method(:length).receiver end # Method#nameはメソッド名 expect "length" do "recv".method(:length).name end # Method#ownerはメソッドのクラス名 expect String do "recv".method(:length).owner end # UnboundMethod#nameはメソッド名 expect "length" do String.instance_method(:length).name end # UnboundMethod#ownerはメソッドのクラス名 expect String do String.instance_method(:length).owner end # GC.stress / GC.stress= も使えるようになった。 # Array#flattenの引数で平滑化レベルを指定できるようになった。便利！ expect [1, 2, [3]] do [1, [2, [3]]].flatten(1) end expect [1, 2, 3] do [1, [2, [3]]].flatten(2) end # Array#shuffle, Array#shuffle!が使えるようになった。 expect [3,2,1,4] do srand 10 # 乱数の種を指定して常に同じ結果を得るように。 [1,2,3,4].shuffle end expect [3,2,1,4] do srand 10 # 乱数の種を指定して常に同じ結果を得るように。 a = [1,2,3,4] a.shuffle! a end # Array#sampleでランダムな要素を得る。 expect 4 do srand 10 [1,2,3,4].sample end expect [4,1] do srand 10 [1,2,3,4].sample(2) end # Array#permutationは順列を得る。ブロックをつけないとEnumeratorになる。 expect Enumerable::Enumerator do [1,2,3].permutation.class end expect [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] do [1,2,3].permutation.sort.to_a end expect [[1],[2],[3]] do [1,2,3].permutation(1).sort.to_a end expect [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]] do [1,2,3].permutation(2).sort.to_a end # Array#combinationは組み合わせ。ああ、ガキのころに習った確率統計がなつかしい。 expect Enumerable::Enumerator do [1,2,3,4].combination(3).class end expect [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] do [1,2,3,4].combination(3).to_a end # Array#productは直積集合。 expect [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]] do [1,2,3].product([4,5]) end expect [[1, 1], [1, 2], [2, 1], [2, 2]] do [1,2].product([1,2]) # !> method redefined; discarding old next end # Array#pop, Array#shiftは取り除く要素数を指定できるようになった。 expect [3, 4] do [1,2,3,4].pop(2) end expect [1,2] do a = [1,2,3,4] a.pop(2) a end expect [1, 2] do # !> method redefined; discarding old rewind [1,2,3,4].shift(2) end expect [3,4] do a = [1,2,3,4] a.shift(2) a end # Array#index, Array#rindexで条件を示すブロックが指定できるようになった。 expect 1 do [0, 1, 0, 1, 0].index {|v| v > 0} end expect 3 do [0, 1, 0, 1, 0].rindex {|v| v > 0} end # Array#assocがto_aryを使うようになった。 expect [1,2] do obj = Object.new def obj.to_ary() [1,2] end [obj].assoc 1 end # Enumerable#takeは最初のn個を取り出す。 expect [1, 2] do [1,2,3,4].take(2) end # Enumerable#take_whileは条件を満たす間要素を取り出す。 expect [1, 2] do [1,2,3,4,5,6].take_while {|i| i < 3 } end expect [] do [1,2,3,4,5,6].take_while {|i| i > 3 } end # Enumerable#dropは前のn個を取り除いた新しい配列を返す。 expect [3] do [1,2,3].drop(2) end # Enumerable#drop_whileは条件を満たす間の要素を取り除いた配列を返す。 expect [3, 4, 5, 0] do [1, 2, 3, 4, 5, 0].drop_while {|i| i < 3 } end expect [1, 2, 3, 4, 5, 0] do [1, 2, 3, 4, 5, 0].drop_while {|i| i > 3 } end # take, take_while, drop, drop_while はEnumerableでも使える。 expect [1, 2] do (1..6).take(2) end expect [1, 2] do (1..6).take_while {|i| i < 3 } end expect [3, 4, 5, 6] do (1..6).drop(2) end expect [3, 4, 5, 6] do (1..6).drop_while {|i| i < 3 } end # Enumerable#one?は条件を満たすもの（真）がひとつである場合にtrueとなる。 expect true do %w{ant bear cat}.one? {|word| word.length == 4} end expect false do %w{ant bear cat}.one? {|word| word.length >= 3} end expect false do [ nil, true, 99 ].one? end expect true do [ nil, true, false ].one? end # Enumerable#none?は条件を満たすもの（真）がない場合にtrueとなる。 expect true do %w{ant bear cat}.none? {|word| word.length == 5} end expect false do %w{ant bear cat}.none? {|word| word.length >= 4} end expect true do [].none? end expect true do [nil].none? end expect true do [nil,false].none? end # Enumerable#minmaxは最小値、最大値を同時に得る。 expect [1, 6] do (1..6).minmax end # Enumerable#min_by, Enumerable#max_by, Enumerable#minmax_by はブロック評価結果で最小値、最大値をもとめる。 expect 22 do [18, 15, 22, 53].min_by {|x| x % 10 } end expect 18 do [18, 15, 22, 53].max_by {|x| x % 10 } end expect [22, 18] do [18, 15, 22, 53].minmax_by {|x| x % 10 } end # Enumerable#cycleは要素ごとに無限に繰り返す。ブロックをつけないとEnumeratorになる。 expect Enumerable::Enumerator do [1,2,3].cycle end # ちなみにEnumerable#takeは最初の要素n個取り出す。 expect [1,2,3, 1,2,3, 1,2,3, 1] do [1,2,3].cycle.take(10) end # 繰り返す回数を指定できる。 expect Enumerable::Enumerator do [1,2,3].cycle(3) end expect [1, 2, 3, 1, 2, 3, 1, 2, 3] do [1,2,3].cycle(3).to_a end # Enumerable#find_indexは条件を満たす最初のインデックスを求める。 expect nil do (1..10).find_index {|i| i % 5 == 0 and i % 7 == 0 } end expect 34 do (1..100).find_index {|i| i % 5 == 0 and i % 7 == 0 } end # Enumerable#injectでついにSymbolを指定することができるようになったぜ！ # 合計が簡単に記述できるようになってウハウハ。 # しかもreduceというこれまたカッコイイ別名を手に入れたぜ。MapReduceと対になれたよ。 expect 10 do (1..4).inject(:+) end expect 10 do (1..4).reduce(:+) end # Enumerable#countは要素数、条件を満たす要素数の数を数える。 # [2008/05/15] ブロックつきArray#nitemsは削除された。 expect 2 do [1, 2, 4, 2, 10, 9].count(2) end expect 3 do [1, 2, 4, 2, 1, 1].count {|x| x%2 == 0} end # Enumerable#first。 expect 1 do (1..6).first end # Enumerable#group_byはブロックの値をキーとするハッシュにグループ分けする。便利〜 expect({0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}) do (1..6).group_by {|i| i%3 } end # Enumerable::Enumerator#with_indexがあればどんなイテレータもwith_index版になる魔法のメソッド。 expect [["abc

", 0], ["def", 1]] do "abc

def".each_line.with_index.to_a end # Enumerable::Enumerator#nextは次の要素を順次得る。 expect [1, 2] do e = (1..6).each [e.next, e.next] end # Enumerable::Enumerator#rewindは最初の要素に巻戻す。 expect [1, 2] do e = (1..6).each e.next; e.next e.rewind [e.next, e.next] end # String#lines, String#bytesはEnumeratorを返す。これがRuby 1.9流だ。 expect ["abc

", "def

"] do "abc

def

".lines.to_a end expect [97, 98, 99] do "abc".bytes.to_a end # String#chars, String#each_charが使えるようになった。 # もはやsplit(//)なんて書かなくてもよくなった。 expect ["お", "は", "よ", "う"] do "おはよう".chars.to_a end expect ["お", "は", "よ", "う"] do [].tap{|a| "おはよう".each_char{|c| a << c}} end # String#partition, String#rpartitionはセパレータ前、セパレータ、セパレータ後を返す。$KCODEに対応している。 expect ["これ", "は", "ペンです"] do "これはペンです".partition("は") end expect ["123", "|", "456|789"] do "123|456|789".partition("|") end expect ["123|456", "|", "789"] do "123|456|789".rpartition("|") end # String#start_with?, String#end_with? は始まり・終わりの文字列の検査。 expect true do "あいうえお".start_with? "あい" end expect true do "あいうえお".end_with? "お" end # String#slice!で負の数を指定したら例外ではなくてnilを返すようにした。slice同様に。 expect nil do "abc".slice!(-999) end # String#index, rindexでto_strを使うようになった。 expect 3 do obj = Object.new def obj.to_str() "y" end "ruby".index(obj) end expect 3 do obj = Object.new def obj.to_str() "y" end "ruby".rindex(obj) end # String#bytesizeは文字列のバイト数を返す。Ruby 1.8.7ではString#lengthの別名。 # Ruby 1.9のString#lengthは文字数を返すための移行措置。 expect 4 do "hoge".bytesize end # Process.execはexecと同じ？ # Kernel#loopにてStopIteration例外を発生させるとループから抜ける。 expect :exit_from_loop do loop do raise StopIteration end :exit_from_loop end # Enumerable::Enumerator#nextは終端でStopIteration例外が発生する expect StopIteration do g = [1].each g.next g.next end # だからEnumerable::Enumerator#nextとKernel#loopは組み合わせて使える expect :exit_from_loop do g = [1].each loop do g.next end :exit_from_loop end # Range#stepは範囲内の要素を s おきに繰り返す。 expect ["a", "c", "e"] do ("a" .. "f").step(2).to_a end # Dir#inspectがわかりやすくなった。 expect "#<Dir:/tmp>" do Dir.open("/tmp"){|d| d.inspect} end # Integer#odd? Integer#even? 偶奇判定。 expect true do 1.odd? end expect false do 1.even? end expect false do 12.odd? end expect true do 12.even? end # Integer#predは前の数を返す。 expect -1 do # !> ambiguous first argument; put parentheses or even spaces 0.pred end expect 10 do 11.pred end # Integer#ordは文字コード。Ruby 1.9との互換性のため。 expect 97 do 97.ord end expect 97 do ?a.ord end # Hash.[]がto_hashを使うようになった。 expect({1=>2}) do obj = Object.new def obj.to_hash() {1=>2} end Hash[obj] end # 0の累乗 expect(1.0) do 0**0.0 end expect Rational do 0**-1 end expect "Rational(1, 0)" do (0**-1).inspect end # shellwords.rbのメソッド群。 require 'shellwords' expect "a\\\\x" do 'a\\x'.shellescape end expect ["ruby", "-e", "print 1"] do %!ruby -e 'print 1'!.shellsplit end expect "ruby -e print\\ 1" do ["ruby", "-e", "print 1"].shelljoin end # Tempfile.openで拡張子を指定できるようになった。やったー require 'tempfile' expect(/\.rb$/) do t = Tempfile.open(["temp", ".rb"]) t.path end require 'tmpdir' # Dir.mktmpdirは一時ディレクトリを作成する。 expect(/\/foo/) do begin d = Dir.mktmpdir "foo" ensure Dir.rmdir d end end expect(/\/foo.*bar$/) do begin d = Dir.mktmpdir ["foo", "bar"] ensure Dir.rmdir d end end # Dir#each / Dir.foreach にブロックをつけないとEnumeratorを返す。 require 'tmpdir' expect Enumerable::Enumerator do Dir.open(Dir.tmpdir){|d| d.each.class } end expect Enumerable::Enumerator do Dir.foreach(Dir.tmpdir).class end # ObjectSpace.each_objectにブロックをつけないとEnumeratorを返す。 expect Enumerable::Enumerator do ObjectSpace.each_object.class end # Regexp.unionの引数に array of String も受け付けるようになった。 expect(/a|b/) do Regexp.union(["a","b"]) end # 安全な乱数発生器 # require 'securerandom' # SecureRandom.hex(10) # ランダムな16進文字列 # SecureRandom.base64(10) # ランダムなbase64文字列 # SecureRandom.random_bytes(10) # ランダムなバイナリ文字列 end # >> Expectations ................................................................................................................... # >> Finished in 0.01327 seconds # >> # >> Success: 115 fulfilled