Rubyの定数が嫌い

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


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


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



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


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

1つのmixin MNamespaceモジュール、およびそのクラス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 


Source: https://habr.com/ru/post/J347272/


All Articles