[ruby-list:24564] Ruby の落とし穴からのスレッドなどからまとめてみました。
RWikiのRubyの落とし穴に持っていきました。 さらに<URL:http://www.ruby-lang.org/ja/man-1.6/>の付録に移動しました。
結合強度の話
p 1..3.to_a #! ArgumentError # p (1..(3.to_a)) p (1..3).to_a # エラーは出ない # (p (1..3)).to_a p ((1..3).to_a) # これが正解
メソッド呼出のかっこ省略による誤解釈
Time.gm (19+1)*100,2,11,12,34,56 が、(Time.gm (19+1)) * 100,2,11,12,34,56 と解釈されます。例のようにエラーが出る場合ばよいのですが、 出ない場合は厄介なことになったりします。
優先順位の違い
p :a if true || false && false p :b if true or false and false #=> :a
and と or の優先順位が同じなので左から評価されることに注意。
式と文
p (true && true) #=> true p (true and true) #=> parse error p ((true and true)) #=> true
true and falseは文と解釈されるので式として解釈されるための括弧とpの引数をくくる括弧が必要。
[ruby-list:24664]式を要求するコンテキストでは、以下のものが直接は書けません。
ハッシュ引数
p {1=>2} #=> parse error p ({1=>2}) #=> {1=>2} def f (x, y); p [x,y]; end f {1=>2},3 #=> parse error f 2,{1=>2} #=> [2, {1=>2}]
とある do..end 派のわるあがき
(1..5).sort do |a,b| b <=> a end.reverse # parse error (1..5).sort() do |a,b| b <=> a end.reverse # [1,2,3,4,5]
空白に注意
a=1; b = a+2; # a と 2 の和 b = a +2; # a(+2) と解釈される. b = a + 2; # a と 2 の和
文字列展開について[ruby-list:24629]
> ruby -e 'h = {"1" => "foo"}; puts "1".sub(/(\d)/, "#{h[\1]}")' -e:1: compile error in string expansion (SyntaxError) -e:1: parse error h[\1] ^ > ruby -e 'h = {"1" => "foo"}; puts "1".sub(/(\d)/, "#{h[\\1]}")' -e:1: compile error in string expansion (SyntaxError) -e:1: parse error h[\\1] ^ > ruby -e 'h = {"1" => "foo"}; puts "1".sub(/(\d)/, "#{h[\\\1]}")' -e:1: compile error in string expansion (SyntaxError) -e:1: parse error h[\\\1] ^ > ruby -e 'h = {"1" => "foo"}; puts "1".sub(/(\d)/, "#{h[\\\\1]}")' -e:1: compile error in string expansion (SyntaxError) -e:1: parse error h[\\\\1] ^
....書けません :-) こうしときましょ.
> ruby -e 'h = {"1" => "foo"}; puts "1".sub(/(\d)/) { "#{h[$1]}" }' foo
sub(gsub, sub!, gsub!)の引数
"'hoge'".sub("(['\"])(\\w+)\1",'[\2]') #=> "'hoge'" "'hoge'".sub("(['\"])(\\w+)\\1",'[\2]') #=> "[hoge]" "'hoge'".sub("(['\"])(\\w+)\\1","[\2]") #=> "[\002]" "'hoge'".sub(/(['"])(\w+)\1/,'[\2]') #=> "[hoge]" "'hoge'".sub(/(['"])(\w+)\1/) { "[#{$2}]" } #=> "[hoge]"
第1引数に文字列を渡すと正規表現に変換されて使われます。素直に正規表現を渡した方がわかりやすいでしょう。
第2引数は単純な文字列でないのならブロックにして$1などを使った方がわかりやすいでしょう。
ローカル変数と等号付きメソッド
レシーバを省略するとメソッドではなくローカル変数として扱われます。 普通のメソッドはselfのレシーバを省略できますが、この場合は不可。
# 例1 class Test def test=(val) @ival=val end def print() p @ival end def boo() test="PINPON" end end a = Test.new a.test="hogehoge" a.print #=>"hogehoge" a.boo a.print #=>"hogehoge" (not "PINPON") # 例2 class Foo def bar=(v) end def baz bar = 0 # ローカル変数 bar への代入 self.bar = 0 # メソッド bar= の呼出し end end
任意のループを抜ける
catch(:last) { while true (1..5).each{|x| throw :last if x == 3 print "#{x}\n" } end }
16 進数文字列を 10 進数文字列に変換する
i = "f".hex print i, "->" ,format("%x",i)
2進数から変換
'0b10'.oct #=> 2 '10'.oct #=> 8 '010'.oct #=> 8 '0x10'.oct #=> 16
配列への nil の挿入
a = [1,2,3] a[1,1] = nil # a[1.1] = nil.to_a を行っている p a # [1,3] a = [1,2,3] a[1,1] = [nil] p a # [1,nil,3]
これは、nil.to_a が [] (空の配列)を返すために起こります。 ちなみに nil.to_s は "" (空の文字列)を返します。
数値演算の誤差[ruby-list:25173](Rubyに限らない)
(500.5 * 1).round #=> 501 (500 * 1.001).round #=> 500 printf "%10.20f\n", (500.5) #=> 500.50000000000000000000 printf "%10.20f\n", (500*1.001) #=> 500.49999999999994315658
変更しなかったときにnilを返す。
'a'.sub!(/a/,'b') #=> "b" 'b'.sub!(/a/,'b') #=> nil
組み込みクラスへの破壊的メソッドの追加
class String def sample! self.replace('sample') # self = 'sample' # これは誤り end end
Hash.new([])は同じ[]を参照するので…。
h = Hash.new([]) h[0] << 0 h[1] << 1 p h #=> {} p h.default #=> [0, 1]
<< は破壊的メソッドなので代わりに += を使えば毎回新しいインスタンスが生成されるが効率が悪い。
配列内の値の変換
a = [1,2,3] a.each{|item| item = item + 10} # 誤り a.collect!{|item| item + 10}
代入した時点で元の item (配列に格納されているもの)とは別の オブジェクトをさすためです。従って、
a = ['a','b','c'] a.each{|item| item.replace('x')}
など、破壊的メソッドは有効となります。
メソッド呼び出しで 配列を展開した変数列を渡す
def sum(a,b,c) print a+b+c,"\n" end a = [1,2,3] sum(*a) # -> sum(1,2,3) -> 6
同様にProcをブロックとして渡せる。
def foo yield(2) end pr = proc {|i| i*i} foo(&pr) #=> 4
逆に仮引数でまとめて受け取ったりProcとして受け取ったりできる。
def foo(*arg, &block) block.call arg[1] end foo(0,1,2) do |i| puts i #=> 1 end def test(*a) @b = [a] #正しくは、@b = a end def a = (b) #正しくは、a=(b) @a = b end
superとsuper(...)の違い。
class Foo def initialize end end class FooFoo < Foo def initialize(a) super end end
上のはバグります。 正解は、
class Foo def initialize() end end class FooFoo < Foo def initialize(a) super() end end
時間待ちの処理
require 'timeout' begin timeout(5) do $stdin.gets end rescue TimeoutError print "timeout\n" end
finalizer で Mutex は注意[ruby-list:24697]
finalizer で登録したブロックの中で、Mutex などで待ちに入るとデッドロックします。 理由はまだ自分で整理しきれてないのですが、おそらく、同じ Mutex を他でもロックしていると、そのロック中の GC で finalizerが 呼ばれてデッドロックするのかな、と思います。 weakref.rb みたいに Thread.critical を使う方が安全です。
File を open してほったらかしにするようなコーディングをしていると,たとえば SunOS 4.X みたいな変なシステムでびっくりできます.
stdio でファイルディスクリプタを char でもってる.だもんで,どんどんファイル開いていくと,そのうちファイルディスクリプタが負になってEBADF.
while str = File.open(name).read : end
で close を GC 任せにしたりせず,
while begin f = File.open(name) str = f.read ensure f.close end : end
とか
str = nil while File.open(name) { |f| str = f.read } : end
とか,close が保証されるようにしといた方が,無難.
IO からの読み出しは毎回新たなオブジェクトを生成します.ふつうは平気ですが,意識していないとはた迷惑なプログラムが出来上がる場合があります.
400byte ほどのブロック二つを連結したものが別のバイナリファイルに入 ているかを調べるプログラムを作った時のこと.総ブロック数 400 個ほどを調べるのにスクリプトの中でループしてたら,たまにシステム全体が一分程固まったりして.メモリ足りないとはいえ,これは良くない.
必要に応じて[ruby-list:23467]みたいなものを作る方が良いでしょう.もっと良いのはこんなことに ruby を使わないことかもしれないけど.