3

私は StackOverflow を見回す機会があり、Ruby Koans からよりよく理解しようとしていたこの同じ質問を見つけました ( Ruby Koans: クラス定義の明示的なスコープ パート 2 )。

class MyAnimals
  LEGS = 2

  class Bird < Animal
    def legs_in_bird
      LEGS
    end
  end
end

def test_who_wins_with_both_nested_and_inherited_constants
  assert_equal 2, MyAnimals::Bird.new.legs_in_bird
end

# QUESTION: Which has precedence: The constant in the lexical scope,
# or the constant from the inheritance heirarachy?

# ------------------------------------------------------------------

class MyAnimals::Oyster < Animal
  def legs_in_oyster
    LEGS
  end
end

def test_who_wins_with_explicit_scoping_on_class_definition
  assert_equal 4, MyAnimals::Oyster.new.legs_in_oyster
end

# QUESTION: Now Which has precedence: The constant in the lexical
# scope, or the constant from the inheritance heirarachy?  Why is it
# different than the previous answer?

リンクの説明に基づいて、他の人 (私を含む) が持っていた主な混乱は、クラス定義が原因だったようです:

class MyAnimals::Oyster < Animal
  # stuff goes in here
end

私の当初の考えでは、MyAnimals::Oyster は Oyster クラスが MyAnimals 内で定義されたことを意味するというものでした。つまり、上記のコードは次のコードに似ていると思いました。

class MyAnimals
  class Oyster < Animal
    # stuff goes in here
  end
end

私の考えをテストするために、IRB で次のことを行いました。

class MyAnimals
  LEGS = 2

  class Bird < Animal
    def legs_in_bird
      LEGS
    end
  end
end

class MyAnimals::Oyster # You should notice that I'm not inheriting from Animal anymore
  def legs_in_oyster
    LEGS
  end
end

私の推論が正しければ、以下のコードは 2 を返すと予想されます。

MyAnimals::Oyster.new.legs_in_oyster # => NameError: uninitialized constant MyAnimals::Oyster::LEGS

これは 2 を返さないので、なぜ 2 を返さないのか説明してもらえますか?

編集: Animal クラスの追加を怠りました。ここにあります:

class Animal
  LEGS = 4
  def legs_in_animal
    LEGS
  end

  class NestedAnimal
    def legs_in_nested_animal
      LEGS
    end
  end
end
4

2 に答える 2

3

Ruby が値を検索する順序に戻ります。ステップ1は「囲うスコープ」です。このようにネストされたクラスを定義すると

class Animal
    class Bird
    end
end

「鳥」は「動物」スコープに含まれています。

こうすると

class Animal
end

class Animal::Bird
end

'Bird' を 'Animal' 内にネストされたものとして定義しましたが、'Bird' を定義した時点では、外側のスコープはグローバルスコープでした。IRB に戻ってLEGS = 0(グローバル スコープで) 定義し、Oyster をもう一度試してください。

于 2013-08-21T20:56:23.650 に答える
2

C++ や Java のような厳密に字句的/静的なスコープを持つ言語では、識別子は現在のスコープをチェックするだけで解決され、スコープを 1 つ上に上げて、最も基本的なスコープに到達するまで繰り返します。たとえば、例が C++ の場合、LEGS は最初に呼び出しクラスの Bird/Oyster で検索され、次に Animal、次に My Animal が検索されます。

Ruby には一種の動的スコープがあります。各オブジェクトは、同じ「場所」に存在する場合でも、実行時に定義または作成された方法に応じて、独自のスコープ検索順序を持つことができます。各メソッドは、検索するスコープのスタックを持っていると考えることができ、メソッドがどのように定義されたかによって、新しいスコープがそのスタックにプッシュされます。

Bird の定義方法が原因で (BaseScope は実際の名前ではないため、これを提供するのに十分なコードを提供していません) BaseScope::MyAnimals::BirdBaseScope::MyAnimals::AnimalBaseScope::MyAnimalsおよびBaseScope名前解決を検索します。

一方、最初のオイスターはBaseScope::MyAnimals::OysterBaseScope::MyAnimals::Animalおよびのみを取得しBaseScopeます。

継承のないカキは、さらに少なくBaseScope::MyAnimals::OysterなりBaseScopeます。

この例で class キーワードと継承を使用するたびに、別のスコープがプッシュされ、検索対象のコンテンツをスコープのスタックにチェックインします。したがって、class MyAnimals::Oysterこのスタックにプッシュされたエントリは 1 つだけです。

編集

簡単にするために、legs_in_oyster メソッドは省略しました。検索できる範囲です。それは自明な定義であり、それを含めると、この回答に多くの役に立たないテキストが追加されます。

また、単純化のためにグローバル スコープも省略しました。Koans には、BaseScope とグローバル スコープまたはその中間に少なくとも 1 つのスコープがあることを知っています。

于 2013-08-21T20:51:28.890 に答える