Rubyの定数が嫌い

Rubyは非常に複雑なプログラミング言語です。 信じられないほど美しくて読みやすいですが、多くのテーマと機能があり、経験豊富なRuby開発者にとっても「暗い森」のままです。 そのようなトピックの1つは、定数の検索です。







見出しにもかかわらず、記事には怒りはありません。







この投稿の目的は、検索アルゴリズムの詳細な説明ではありません。 目標は、開発者の注意をトピックに引き付けることだと思います。 部分的には、これは魂からの叫びです。









1つの小さな例を検討します。 まず、いくつかの定数を定義しましょう。







module M A = 'm' end module Namespace A = 'ns' class C include M end end
      
      





1つのmixin M



Namespace



モジュール、およびそのクラスC



ありますC



モジュールは、私たちが探す定数A



によって明確に定義されます。







次のコードは何を出力すると思いますか? それらが目立たないように、以下に回答を掲載します。







 puts Namespace::C::A module Namespace class C puts A end end
      
      





次に、いくつかのメソッドを定義しましょう。







 module M def m A end end module Namespace class C def f A end end end class Namespace::C def g A end end x = Namespace::C.new puts xf puts xg puts xm
      
      





それらの間に違いがあると思いますか?







答え



以下に、コメントの回答を含むサンプルの完全なコードを示します。







 module M A = 'm' end module Namespace A = 'ns' class C include M end end puts Namespace::C::A # m module Namespace class C puts A # ns end end module M def m A end end module Namespace class C def f A end end end class Namespace::C def g A end end x = Namespace::C.new puts xf # ns puts xg # m puts xm # m
      
      





すなわち プログラムの出力は次のようになります。







 m ns ns m m
      
      





ミニ説明



つまり、定数の検索はいくつかの段階で行われます。







  1. いわゆるで検索 レキシカルスコープ 。 すなわち 現在のコード行が定義されている場所に応じて検索が行われます。 たとえば、最初の出力では、インタープリターは最上位にあり、定数Namespace::C::A



    を表示します。2番目の出力では、最初にNamespace



    モジュールに入り、次にC



    クラスに入り、その後puts



    ます。 ネスト、特にModule.nesting



    メソッドについて読むことで、これについて詳しく知ることができます。
  2. 最初の段階が成功しなかった場合、インタープリターはミックスインと親クラスの「ポーリング」を開始します。 最初の段階でポーリングされたモジュールごとに。
  3. 前の手順が失敗した場合、最上位がチェックされます。 実際、この項目は省略できます。 本質的に2番目に含まれています。 トップレベルはクラスObject



  4. この段階では、定数は見つからないとみなされ、 const_missing



    メソッドはconst_missing



    類推によってconst_missing



    れます。 この方法は、Ruby on Railsでコードの自動読み込みと再読み込みに使用されると思います。


このように:







 #    . #       #       M puts Namespace::C::A # m module Namespace class C #   Namespace -> Namespace::C #       Namespace puts A # ns end end module M def m #    M.       A # m end end module Namespace class C def f #    Namespace -> Namespace::C A # ns end end end class Namespace::C def g #    Namespace::C (  Namespace   ) #      #          A # m end end
      
      





おわりに



コード内に定数を記述すると、実行コンテキストとは関係なく、記述されたコードと比較して値を計算するようにRubyが強制していると言えます(非常に奇妙に聞こえますが、申し訳ありません)。







Rubyスタイルガイドでは、1つの適切なルールを定義しています。

ネストされたクラス/モジュールを明示的に定義および再発見します。 すなわち class A::B



を記述する必要はありませんclass A::B



この単純なルールは、驚きを避け、ほとんどの場合、定数を見つけることをまったく考えないために十分です。







あなたが読むことができるもの:









更新する



ユーザーDsideSPbは、定数検索の追加機能に関する有用なコメントを 提供しました。 確かに、最新(2.5.0)リリースで削除されました。







個人的には、すべての詳細はわかりませんが、状況によっては、定数に間違ったパスを指定した場合、インタープリターはそれをトップレベルのパスに置き換えることができます。 ただし、これはすべての場合に機能するわけではありません。







 # 1.rb class A; end class B; end A::B #  B,    # 2.rb class A; end module M; end A::M # ==> M   M::A # ==> NameError
      
      






All Articles