2

これがあなたの麺を竹で割る質問です。

class_evalクラスをレシーバーとして使用して評価されるブロックでは、メソッド宣言と定数宣言に違いがあるようです。不一致を示すコードは次のとおりです。

module X
  def save_block(&block)
    @block = block
  end
end

module Y
  extend X
  save_block do
    SOME_CONSTANT = 1
    def meth
      "a method"
    end
  end
  def self.included(m)
    m.class_eval &@block
  end
end

class Z
  include Y
end

class W
  include Y
end

少なくともRuby1.9.3でこのコードを実行すると、次のエラーが発生します。

warning: already initialized constant SOME_CONSTANT

私はそれを予期していませんでした。最初にどこmethにあるかを考えてみましょう。

Z.instance_methods(false)
=> [:meth]

W.instance_methods(false)
=> [:meth]

そしてY、インスタンスメソッドはありません:

Y.instance_methods(false)
=> []

ブロックはのレシーバーとして実行Zされるため、これは理にかなっています。ただし、定数が異なるため、次のようなエラーメッセージが表示されます。Wclass_eval

Y.constants(false)
=> [:SOME_CONSTANT]

Z.constants(false) 
=> []

W.constants(false)
=> []

ここで、定数はY(何らかの奇妙な理由で)で定義されることになります。したがって、ブロックが2回実行されるときに、定数SOME_CONSTANTはすでに定義されています。

啓蒙をとても楽しみにしています。

更新(2012/11/19):

@phoetが以下で指摘しているように(彼の回答に対する私のコメントへの応答)、定数はブロックの字句スコープ、つまりブロックが最初に定義されたスコープで定義されます。私の例では、これはYになります。

4

1 に答える 1

1

この質問で説明されているように、定数定数処理へのアクセスは、その「字句スコープ」内にあります。つまり、実行されるコンテキストではなく、定義されるコンテキストを意味します。

于 2012-11-20T20:00:53.170 に答える