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
ミニ説明
つまり、定数の検索はいくつかの段階で行われます。
- いわゆるで検索 レキシカルスコープ 。 すなわち 現在のコード行が定義されている場所に応じて検索が行われます。 たとえば、最初の出力では、インタープリターは最上位にあり、定数
Namespace::C::A
を表示します。2番目の出力では、最初にNamespace
モジュールに入り、次にC
クラスに入り、その後puts
ます。 ネスト、特にModule.nesting
メソッドについて読むことで、これについて詳しく知ることができます。 - 最初の段階が成功しなかった場合、インタープリターはミックスインと親クラスの「ポーリング」を開始します。 最初の段階でポーリングされたモジュールごとに。
- 前の手順が失敗した場合、最上位がチェックされます。 実際、この項目は省略できます。 本質的に2番目に含まれています。 トップレベルはクラス
Object
- この段階では、定数は見つからないとみなされ、
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
この単純なルールは、驚きを避け、ほとんどの場合、定数を見つけることをまったく考えないために十分です。
あなたが読むことができるもの:
- 「Rubyプログラミング言語」の第7.9章-彼らが言うように、すべてを直接学ぶ。
- Railsの定荷重ガイド
- Rubyスタイルガイド
- 読むのではなく、 Module.nestingで遊ぶ
- ?
更新する
ユーザー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