7

instance_eval関連するブロックへの引数としてyieldがあることに気付きselfました (1.9.2 バージョンのバグを除く: http://www.ruby-forum.com/topic/189422 )

1.9.3p194 :003 > class C;end
1.9.3p194 :004 > C.new.instance_eval {|*a| a}
 => [#<C:0x00000001f99dd0>] 
1.9.3p194 :005 > 

これはどこかに文書化/仕様化されていますか? ruby-doc:BasicObjectを見ると、言及されているブロック パラメータが見当たりません。

とにかくそれ自身が常に定義されているときに明示的に渡す理由はありますか?


私がこれに打たれた方法は次のとおりです。

l = lambda {  }
myobj.instance_eval(&l)  # barks

これは 1.8.x では問題なく機能しました (ブロックのアリティが強制されていなかったためだと思います)。

その後、1.9.2 にアップグレードしましたが、それでも機能しました。これは、ラムダ ブロックの引数が厳密に適用されているにもかかわらず (そのため、self の引数を宣言しないと不平を言うことになります)、奇妙な偶然の一致ですが、上記のリンクにあるバグにより、self は実際にはこのバージョンでは渡されませんでした..

次に、そのバグが修正された1.9.3にアップグレードしたため、引数エラーがスローされ始めました-マイナーバージョン変更のIMHOにはかなり驚くべきことです。

したがって、1 つの回避策は、パラメーターを宣言するか、代わりにラムダをブロックにすることです。

 l = proc {  }
  myobj.instance_eval(&l) # fine

これが適切に文書化されるまで、他の人が私のように時間を無駄にするのを避けるために、完全なストーリーを説明することを考えただけです.

4

3 に答える 3

3

Rubyのソースコードを読んで、私が解釈できるのは次のとおりです。

instance_eval はこれを実行しています:

return specific_eval(argc, argv, klass, self)

次に実行されます:

 if (rb_block_given_p()) {
     if (argc > 0) {
         rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
     }
     return yield_under(klass, self, Qundef);
 }

QundefVALUES 引数に渡されていることがわかります。

if (values == Qundef) {
    return vm_yield_with_cref(th, 1, &self, cref);
}

その特定のコード行で、argc (引数カウント) を手動で 1 に設定し、引数を「self」に設定しました。後でブロックを準備するコードは、ブロックへの引数をこれらの引数に設定するため、最初の引数 = "self" で残りは nil です。

ブロック引数を設定するコードは次のことを行っています。

   arg0 = argv[0];

   ... bunch of code ...

     else {
         argv[0] = arg0;
     }

     for (i=argc; i<m; i++) {
         argv[i] = Qnil;
     }

その結果:

1.9.3p194 :006 > instance_eval do |x, y, z, a, b, c, d| x.class end
 => Object 
1.9.3p194 :008 > instance_eval do |x, y, z, a, b, c, d| y.class end
 => NilClass 

なんで ?わかりませんが、コードは意図的なようです。実装者に質問をして、彼らがそれについて何を言わなければならないかを見てみるといいでしょう。

[編集]

これはおそらく、instance_eval に渡すブロックが作成されている場合と作成されていない場合があるためです (コードは、ブロックで変更するクラスに設定されている self に依存します)。インスタンスを引数として変更する必要があり、このようにして instance_eval でも機能します。

irb(main):001:0> blk = Proc.new do |x| x.class end
#<Proc:0x007fd2018447b8@(irb):1>
irb(main):002:0> blk.call
NilClass
irb(main):003:0> instance_eval &blk
Object

もちろん、これは単なる理論であり、公式文書がなければ推測することしかできません。

于 2012-09-28T22:37:55.463 に答える
1

主に文字列の評価を目的とした #instance_eval とは異なり、主にブロックの評価を目的とした #instance_exec には、説明されている動作がないことを発見しました。

o = Object.new
o.instance_exec { |*a| puts "a.size is #{a.size}" }
  => a.size is 0

これはおそらく意図しない不整合であるため、バグを発見した可能性があります。Ruby bugsに投稿してください。

于 2012-09-28T22:50:37.280 に答える