質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

解決済

3回答

911閲覧

class A::Bとクラス定義を行ったとき、クラスBはクラスAのネストの中にあると考えて良いのか。

Pyons04

総合スコア19

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

1クリップ

投稿2019/12/06 07:35

編集2019/12/06 07:52

昨日も似たような質問をさせていただいたものです。
Rubyの定数探索について勉強しているのですが、全く理解できず困っています。

ruby

1## Code 1 2class A 3 CONST = "Hello, world" 4end 5 6class A::B 7 def method 8 CONST 9 end 10end 11 12p A::B.new.method # ruby.rb:7:in `method': uninitialized constant A::B::CONST (NameError)

以上のようなコードで、定数CONSTが参照できないのは何故でしょうか。

自分の考え

Rubyの定数探索は公式ドキュメントによると

定数参照時の定数の探索順序は、最初にネスト関係を外側に向かって探索し、次に継承関係を上位に向かって探索します。

公式ドキュメント

とされています。Bクラスのメソッドmethodが呼ばれたとき、Rubyは同クラス内には定数が定義されていないので、ネスト関係の外側を見に行くと思います。

クラスBは、クラスAの中のネストの中に定義したので(class A::B)、クラスBの外側とはclass Aのはずです。クラスAは既に定数CONSTが定義されているので、これを参照することが出来るはず。

と考えてしまうのですが、実際にはruby.rb:7:in `method': uninitialized constant A::B::CONST (NameError)というエラーが発生してしまいます。

一方、クラスBの定義の位置を以下のようにすると、参照が可能です。

ruby

1## Code 2 2class A 3 CONST = "Hello, world" 4 class B 5 def method 6 CONST 7 end 8 end 9end 10 11p A::B.new.method # "Hello, world"

class A::Bという定義をしたとき、Rubyでは実際に何が起こっているのでしょうか?Code2のようにクラスAの中のネストにクラスBを定義させるということにはならないのでしょうか?

また

Rubyの定数探索はまず__レキシカルスコープ__を辿る

という言葉をどこかで聞いたのですが、__ レキシカルスコープ__ とは「Aの中に定義されたB」というCode1のような関係(スコープ)のことは指さないのでしょうか?

昨日に引き続き質問を連投してしまい恐縮ですが、ご回答いただけると幸いです。
よろしくお願いいたします。なお、Rubyは2.1.0を使用しております。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

2つのクラスの関係で、
・オブジェクト指向的な意味での継承関係
A::Bのような名前空間の修飾関係(正式には何というべきか?)
・宣言の書き方でのネスト関係

は、それぞれ別物です。ネストすれば普通は修飾されますが、下記のように修飾されないことも可能です。

公式ドキュメントの、

最初にネスト関係を外側に向かって探索し

は、3つ目の関係のことです。

Ruby

1FOO="global constant" 2class A 3 FOO="class A constant" 4 class B 5 def foo 6 p [1, self.class, FOO] 7 end 8 end 9 class ::C 10 def foo 11 p [2, self.class, FOO] 12 end 13 end 14end 15class A::D 16 def foo 17 p [3, self.class, FOO] 18 end 19end 20 21A::B.new.foo #=> [1, A::B, "class A constant"] 22C.new.foo #=> [2, C, "class A constant"] 23A::D.new.foo #=> [3, A::D, "global constant"]

投稿2019/12/06 16:11

otn

総合スコア84423

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

winterboum

2019/12/06 22:44

[例えばrailsでは models/a.rb の class A に定義したものが models/a/b.rb に定義した class A::B で参照できます。ここの仕組みまでは理解していないので] ここはどんな仕掛けになっているのでしょう
Pyons04

2019/12/07 08:14 編集

otn 様 ご回答誠にありがとうございます。 また返信が遅くなりまして、申し訳ありません。 「名前空間の修飾関係」と「宣言した場所のネスト関係」を完全に同じものだと勘違いしておりました。 クラス定義内の定数の探索経路はあくまで「宣言した場所のネスト関係」に基づいていると考えれば、この挙動にも非常に納得がいきます。 サンプルコード付きで丁寧に解説してくださりありがとうございました。 winterboum 様 ご回答ありがとうございます。返信が遅くなりまして申し訳ありません。 モジュール外部から(定数やモジュール関数などへ)アクセスする際に「名前空間の修飾関係」を指定しているだけで、クラス宣言自体はトップレベルで行われている。(修飾関係に応じてアクセスする際に必要な名前が変わってくるだけ)ということではないでしょうか。 自分は今のところ以上のように解釈しております。
guest

0

それはネストしていません。
class A class A::B end end
がネストです。

投稿2019/12/06 07:55

winterboum

総合スコア23284

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Pyons04

2019/12/06 08:25

winterboum様 迅速なご回答ありがとうございます。たしかに、class Aを再オープンしてclass A::Bを定義すると期待通り動作しました。 追加の質問で恐縮なのですが、Code1ではクラスBはどこに定義されていることになるのでしょうか。A::B という名前のクラスがトップレベルに定義されていると考えればよいのでしょうか。(つまりコロン::を含めてクラス名となって文法的意味を持たないということでしょうか。)
winterboum

2019/12/06 09:32

そこあまり自信ありません。 例えばrailsでは models/a.rb の class A に定義したものが models/a/b.rb に定義した class A::B で参照できます。ここの仕組みまでは理解していないので、railsが何か工夫しているのか、rubyの機能にあるのあ。。 ここは識者の智慧をお借りしたい
guest

0

クラスA::BはクラスAを継承していない。パスを示せばクラスAの定数を参照できる。
クラスBはクラスAを継承している。クラスAの定数を参照できる。
クラスM::BはモジュールMを名前空間にして、クラスAを継承している。クラスAの定数を参照できる。
クラス名はただの定数なので、パスが異なれば同じ名前のクラスを定義できる。
クラスA::BもクラスBもトップレベルに定義されている。クラス名がA::Bだからといって継承関係はない。
クラスM::Bはトップレベルに定義されていない。

Ruby

1class A 2 CONST = "Hello, world" 3end 4 5class A::B 6 def method 7 A::CONST 8 end 9end 10 11p A::B.ancestors 12p A::B.new.method 13 14class B < A 15 def method 16 CONST 17 end 18end 19 20p B.ancestors 21p B.new.method 22 23module M 24 class B < A 25 def method 26 CONST 27 end 28 end 29end 30 31p M::B.ancestors 32p M::B.new.method 33

追加
以下のクラスA::BはクラスAを継承していないが、ネスト関係の外側にCONSTが存在するため参照できる。

Ruby

1class A 2 CONST = "Hello, world" 3 class B 4 def method 5 CONST 6 end 7 end 8end 9 10p A::B.ancestors 11p A::B.new.method 12

投稿2019/12/06 14:09

編集2019/12/06 14:44
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2019/12/06 15:46

「継承」という言葉がオブジェクト指向での継承と違う意味でお使いのようですが、どういう意味でしょう?
退会済みユーザー

退会済みユーザー

2019/12/06 17:10 編集

以下のページに書いてある「継承」という操作のことを意味しています。 今回の場合「クラスを継承する」等の操作をしないと、 「継承関係を上位に向かって探索」してもCONSTが見つからないので それを示した方がよいと思い記述しました。 「オブジェクト指向での継承」という言葉については知りません。 クラスを継承する https://www.javadrive.jp/ruby/inherit/index1.html
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問