2012/01/07

ensure節は伝播する例外や戻り値を変える


プログラミング言語 Rubyより

ensure節は、他の制御移転処理を実行して例外が伝播するのをキャンセルすることができる。ensure節が新しい例外を生成すると、元の例外の代わりに新しい例外が伝播する。ensure節にreturn文が含まれていれば、例外の伝播はそこで止まり、ensure節を含むメソッドは呼び出し元に戻る。

プログラミング言語 Ruby 第2版 169ページ ensure節

begin
  return 1 # 呼び出し元に戻る前にensure節にジャンプする
ensure
  return 2 # 戻り値をこの新しい値に置き換える
end
うわー、気をつけないとトラブルなりそう...。

2012/01/03

rescue節は新しい変数スコープを定義しない

プログラミング言語 Rubyより

rescue節は新しい変数スコープを定義しないので、rescue節で名前を与えられた変数は、rescue節終了後も見えることに注意しよう。

プログラミング言語 Ruby 第2版 165ページ 例外オブジェクトの命名

え?まじで?かなり直感に反するんですが。

begin
  a = b
rescue => ex
  puts ex # undefined local variable or method `b' for main:Object                      
end
puts ex  # undefined local variable or method `b' for main:Object                       

まじだった...。なんでこんな仕様なんだろう...。

2012/01/02

出番があるようでないnext

プログラミング言語 Rubyより

returnを囲っているメソッドは、returnを呼び出してきたメソッドとは必ずしも同じではない。ブロック内でreturn文を使うと、ブロックが終了するだけでは話は終わらない。そして、ブロックを呼び出したイテレータが終了してもまだ話は終わりではない。return文があると、いつでもreturnを囲っているメソッドが終了するのである。囲っているメソッドは、字句的に囲っている(exically enclosing)メソッドとも呼ばれ、ソースコードを見たときにブロックを囲っているメソ\ッドのことである。

プログラミング言語 Ruby 第2版 153ページ return
def find(array, target)
  array.each_with_index do |element, index|
    return index if (element == target) # findから戻る
  end
  nil # 要素が見つからなければnilを返す
end

これに対して、ブロック内では、nextの後の式や式リストは、ブロックを呼び出したyield文の「戻り値」になる。nextの後に式がなければ、yieldの値はnilになる。

# 157ページより
squareroots = data.collect do |x|
  next 0 if x < 0 # 負数に対しては0を返す
  Math.sqrt(x)
end

ただ、上のコードは、例えば次のように書き換えられる。

squareroots = data.collect do |x|
  if (x < 0) then 0 else Math.sqrt(x) end
end

うーん。nextの使い道が難しいw

ブロックへの引数渡し

プログラミング言語 Rubyより

引数が1個のブロックを宣言すれば、多重代入のルールにより、2個の値が自動的に配列にまとめられるはずだと予想される。しかし、現実にはそのようには動作しないのだ。

プログラミング言語 Ruby 第2版 150ページ ブロックへの引数渡し
def tow; yield 1,2; end # 2個の値を渡すイテレータ
two {|x| p x} # Ruby 1.8: 警告を発して[1,2]を出力
two {|x| p x} # Ruby 1.9: 警告なしで1を出力
two {|*x| p x} # 両バージョン: 警告なしで[1,2]を出力
two {|x,| p x} # 両バージョン: 警告なしで1を出力

2012/01/01

ブロックと変数のスコープ

プログラミング言語 Rubyより

囲っているスコープの変数を意図せずに書き換えてしまう場合もある。特に、Ruby1.8のブロックのパラメータでは、この問題が起きやすい。Ruby1.8では、ブロックパラメータが既存の変数と同じ名前だと、ブロックが実行されたときに、新しいブロックローカルな変数が作られるのではなく、既存の変数に値が代入されてしまう。

プログラミング言語 Ruby 第2版 148ページ ブロックと変数のスコープ
1.upto(10) do |i|
  1.upto(10) do |i|
    print "{i} "
  end
  print " ==> Row #{i}\n" # 行番号を出力しているつもりだが、列番号になっている
end