REx模擬試験誤答一覧 その6と7
第六回目は88点 第七回目は86点でした。
1回目 (12/7実施)
問17: Refinement
Refinement#usingはメソッドの中では呼び出せない。クラス内だけ。
問18: include時の定数探索
includeが行われたとしても、それはそのクラスのレキシカルスコープに属したことにはならない。 今回のコードは以下のようなものだが、モジュール内のメソッドをインスタンスメソッドとして扱えるようになっても、スコープそのものは変わらないので、例外が発生する。
module M def refer_const CONST end end class C include M CONST = 'Hello World' end c = C.new p c.refer_const
問27: 名前空間の修飾と定義時のネスト関係
名前空間の修飾関係class A::B
と、定義される場所のネスト関係は全く別物である。
なのでModule.nesting
で返却される値も異なる。
module A module B p Module.nesting # [A::B, A] module C p Module.nesting # [A::B::C, A::B, A] end end end module A::B p Module.nesting # [A::B] end
定義された場所のネストの深さに応じて配列の長さは変化する。 一番最初に名前空間の修飾関係が来て、その後ろにその外側のネストの名前空間の修飾関係が来る。
問50: newメソッドはどこに定義されている?
newメソッドは、全てのクラスの特異メソッドとして利用でいきている。
継承関係の最上位のクラスの特異クラスとして定義されていれば、そこで定義された特異メソッドは継承関係を通じて下のクラスも利用できる。特異クラスの継承関係の最上位はClass class
なので(下図)、少なくともClass class
で定義されていると推測できる。
このことから最初の2行の結果は推測できる。
puts Class.method_defined? :new puts String.method_defined? :new
Classクラスにメソッド定義があるので、一行目はtrue。このクラスは各クラスの特異クラスを通じて継承されているので、2行目はfalse。
puts Class.singleton_class.method_defined? :new puts String.singleton_class.method_defined? :new
4行目は一般的なクラスの特異クラス(#String class)を指しているので、ここはClass class
から継承されたnew
が使えると思って間違いなさそう。
3行目はこの継承関係とは全く別に、Class自体にもnew
メソッドが必要であるため、classクラスの特異クラス
にもnew
を定義して使えるようにしている。
第2回 (12/9実施)
問19: instance_eval
選択肢2番目。instance_eval
は本来レシーバーをオブジェクトに、オブジェクトに対して新しい特異クラスを生成し、それに対して新メソッドの追加やメソッドのオーバーライドを可能にしている。
今回はモジュールをレシーバーにして使っているため、これによってinstance_evalはモジュールの特異クラスを生成することになる。
ただし、instance_eval
はmodule_eval
と異なり、ブロックを渡しても文字列を渡しても、評価に違いはない。
module_evalの説明 => instance method Module#class_eval (Ruby 1.8.7) instance_evalの説明 => instance method Object#instance_eval (Ruby 1.8.7)
つまり、instance_evalに対して文字列を渡しても、モジュールの定義と同じスコープを開くことにはならない。 よって定数の参照には失敗する。
module M p Module.nesting # [M] CONST = "Hello, world" end class << M p Module.nesting # [#<Class:M>] end M.instance_eval(<<-CODE) def say CONST end p Module.nesting # [#<Class:M>] CODE p M::say # (eval):2:in `say': uninitialized constant Module::CONST (NameError)
そしてそれを裏付けるように選択肢4番目はmodule_eval
に文字列を渡していて、文字列でクラスの特異クラスと定数の宣言を行っている。module_eval
に文字列を渡すときは(宣言位置的)スコープの変更が行われているので、定数の取得に成功している。
定義済みのモジュールのメソッドや定数に書き手が自分から(宣言位置的)スコープを変更しなくてもアクセスできるようになるのはmodule_eval + 文字列 だけの特例と考えたほうがよさそう。
選択肢1番目は、特異クラスにメソッドを宣言するにあたって、事前にモジュールの再オープンが行われている。そのため、スコープはモジュールに移っているので、モジュールの定数の取得に成功している。
最後に選択肢の3番目は特異クラスの宣言をトップレベルで行おうとしているので、これもスコープの違いから定数の取得に失敗している。
問題21: aliasとalias_methodの違い
aliasとalias_methodは別のメソッドで挙動がことなる。
alias => リテラル(そのまま)かシンボルで指定し、間にコンマは付けない。 alias_method => シンボルか文字列で指定し、間にコンマを付ける。
aliasはキーワードであるのに対してalias_method
は通常のメソッドである。メソッドに対して引数を渡すときは当然ハッシュか文字列になるし(リテラルを渡す文字列などない。)複数の引数を渡すならコンマが必要になる。
問題43: モジュールに対するinstance_eval
これは問題19でも取り扱ったように、モジュールに対するinstance_eval
は特異クラスの定義になる。
今回はinstance_eval
で渡した文字列内に定数が定義されているので、スコープによる定数参照の問題は起こらない。
問46: attr_メソッド
複数選択の問題で1つ(三番目)しか選択出来ていなかった。 まず、一番最後の選択肢はメソッドのループが発生してしまう。
2番目の選択肢もsuper
の指す先(継承関係の上側)に同名のメソッドがないためNo method errorとなる。
最後に一番目の選択肢はattr_reader
で設定したインスタンスメソッドのgetterメソッドだけを、alias_method
で命名変更することに成功している。新しいgetterメソッドの名前はoriginal_name
となり、そのあとに別のname
メソッドを追加してもオーバーライドがされない。(異なるメソッドとして機能する。)
よって期待通りの挙動をする。以下は疑似コード。
class Men attr_reader :name alias :name_getter :name def name "Professor. " + name_getter end def initialize(name) @name = name end end man = Men.new("mizukami") puts man.name # Professor. mizukami