21

rubyで, , などのステートメントを挟まbegin...endずにブロックが使われているのを時々見かけます。例えば:rescueelseensure

foo = begin
   whatever = 3
   "great"
   42
end

コーダーの意図は、begin...endブロック グループ化の品質のためだけにブロックを使用することです (あたかもであるかのようbegindo)。個人的には、このような使用法は、最小の驚きの原則に違反していると思います (begin例外処理を意味します)。

このように使用すると、意図しない結果が生じることはありますbegin...endか? begin...endブロックには、この使用法を危険にするセマンティックな違い (おそらく例外処理で?) がありますか?

Ruby の構文は信じられないほど巧妙であり、ここに奇妙な落とし穴が待っていても驚かないでしょう。

4

1 に答える 1

37

変数に何かを代入したいが、最初に代入したい値を計算しなければならない場合に、これを時々使用します。これにより、コードが少し整頓されます。ユーザーの好みだと思います。基本的にあなたはこう言っています: foo に何かを割り当てていますが、必要な値を取得するには、まずいくつかのことを行う必要があります。メモ化を行うときに特に便利です。

if @cache.nil?
  do_something!
  @cache = read_value
end

できるよ

@cache ||= begin
  do_something!
  read_value
end

ここで利用しているのは、Ruby インタープリターにはスタックがあり、各式は通常、スタックに何かをプッシュするか、スタックから何かを取得します。割り当ては、スタックから最後のものを取得して割り当てます (この場合は、begin/end の最後の行)。多くの場合、これ (Ruby のスタック アプローチ) を知っておくと役立ちます。

私はそれが少なくとも驚くべきことではないと思いますが、それを使用するかどうかはユーザーの好みだと思います.

Ruby MRI 1.9 で生成されるバイトコード命令を見ると、予期しないことは何もしないことがわかります。

 RubyVM::InstructionSequence::compile("c = begin; a = 5; 6; end").to_a

 [:trace, 1],
 [:trace, 1],
 [:putobject, 5],
 [:setlocal, 2],
 [:trace, 1],
 [:putobject, 6],
 [:dup],
 [:setlocal, 3],
 [:leave]

Trace はスタック トレース専用です。無視してかまいません。Dup は、スタックの最後のアイテムを複製します。この例では、ローカル変数の番号は で、ローカル変数a2番号はcです3(したがってputobject, 2、 variable などに割り当てられますa)。これと比較した場合の唯一の副作用a = 5; c = 6dup命令です。つまり、メソッドのスタック サイズが 1 スロット大きくなります。ただし、これは特に重要ではありません。インタープリターがこの特定のメソッド内にある場合にのみ効果があり、スタックのメモリがとにかく事前に予約されているため、スタックポインターがそうでない場合よりも 1 だけ減分されることを意味するだけです。なので、基本的には変わりません。最適化をオンにすると、dupおそらく消えます。

于 2012-11-07T22:23:44.710 に答える