RubyGoldを受験した結果を振り返る
RubyGoldを受験してきましたので、備忘録としてまとめます。
経緯
2022年7月(当時大学4年生)に自分のスキルアップのために、RubySilverの試験を受験しまして、その時は80/100(合格ボーダーは75)で合格しました。
それから一ヶ月程度経った後に、RubyGoldの試験対策を始め、手始め模擬問題集を見てみましたが、最初の感想は「レベルが違う。。。」でした。(実際に初見で解いて30点しか取れませんでした。。。)
そこから、大学の研究室やアルバイトの合間を縫ってRubyGoldの勉強(後に説明)をしていましたが、気づいたら2023年3月。
「やばい!もう社会人じゃん!」
これはまずいと思いましたよね。
そこから勉強の時間を増やして3ヶ月経過しました。
しかし、結果は落ちました。。。
66点。。。原因はわかっています。それを後半説明します。
勉強方法
箇条書きで説明していきますね。
- 公式問題集で勉強(使用したのは、後ろについている問題集)
- メタプログラミングRubyの1章〜5章までを一周
- Rexで勉強
- このブログを読んでRexにない知識をインプット
個人的に時間がかかったのは、メタプログラミングRubyです。
結構量あるし、何より頭に入ってきづらかった!(個人差あり)
ただ、公式問題集に載っていないのに試験に出てくる知識はメタプログラミングRubyで勉強していくしかなさそうでした。
あとはRexで問題をひたすら解いていくことをお勧めします。
- 問題を解いていく
- 答え合わせをして解説を読む
-
irb
で動作確認
(詳しい原理や機能は、ChatGPTに聞くのをお勧めします。)
この繰り返しです。
Rex
Rexを解いていく中で理解に苦戦した問題5つを紹介します。
第一問
class C
CONST = "Hello, world"
end
ccc = C.new
class D
class << ccc
def say
CONST
end
end
end
p $c.say #=> Hello, world
わかる人にはわかるんだろうけど、なんか苦手意識を持っている。。。
$c
はクラスCのインスタンス。say
メソッドは$c
の特異クラスのメソッド(クラスCのインスタンスの特異クラスのメソッド)。
-
$c
の特異クラスを探索→定数CONST
がない。 -
$c
のスーパークラス(クラスC)を探索→CONST
が定義されている!
なので答えはHello world
となります。(解説を書いていたら理解できた。。。)
第二問
mmm = Module.new
mmm.module_eval do
EVAL_CONST = 100
end
puts "EVAL_CONST is defined? #{mmm.const_defined?(:EVAL_CONST)}" #=> true
puts "EVAL_CONST is defined? #{mmm.const_defined?(:EVAL_CONST, false)}" #=> false
puts "EVAL_CONST is defined? #{Object.const_defined?(:EVAL_CONST)}" #=> true
puts "EVAL_CONST is defined? #{Object.const_defined?(:EVAL_CONST, false)}" #=> true
この問題はmod
のレキシカルスコープ内でEVAL_CONST
が定義されているかいないかの問題となります。
まず、module_eval
で定義された定数はトップレベルで定義されます。そのため、3、4行目の評価式はどちらもtrue
になります。
次に、mod
にEVAL_CONST
はあるか?という話になるのですが、mod
にEVAL_CONST
は定義されていません。ただ、const_defined?
メソッドは第二引数にfalse
を設定すると、「継承元の探索を行わない」。false
を指定しないと、「継承元の探索を行う」という形となります。
つまり、1行目の評価式は継承元の探索を行い、2行目の評価式は継承元の探索を行わないとという形になります。
mod
の継承元は、「Moduleクラス→Objectクラス」となります。ObjectクラスにはEVAL_CONST
が定義されていますので、1行目ではtrue
が返されます。
一方で、2行目の評価式では、継承元の探索が行われないので、mod
のスコープ内を探索したらそれで終了。なので、false
が返されます。
1行目が難しいですよね。
第三問
mmm = Module.new
mmm.module_eval <<-RUBY
EVAL_CONST = 100
RUBY
puts "EVAL_CONST is defined? #{mmm.const_defined?(:EVAL_CONST)}" #=> true
puts "EVAL_CONST is defined? #{mmm.const_defined?(:EVAL_CONST, false)}" #=> true
puts "EVAL_CONST is defined? #{Object.const_defined?(:EVAL_CONST)}" #=> false
puts "EVAL_CONST is defined? #{Object.const_defined?(:EVAL_CONST, false)}" #=> false
これは、第二問の定数の定義をヒアドキュメントを使用して定義した場合です。
ヒアドキュメントで定義した時のEVAL_CONST
はmod
のスコープ内でのみ探索することが可能となります。
つまり、1、2行目の評価式ではtrueが、3、4行目の評価式ではfalseが返されます。
第四問
「同じ結果になる選択肢はどれですか(複数選択)」という問題です。
module M
CONST = "Hello, world"
class C
def awesome_method
CONST
end
end
end
p M::C.new.awesome_method #=> Hello, world
---
選択肢1
module M
CONST = "Hello, world"
end
class M::C
def awesome_method
CONST
end
end
p M::C.new.awesome_method #=>例外が起こります
---
選択肢2
class C
end
module M
CONST = "Hello, world"
C.class_eval do
def awesome_method
CONST
end
end
end
p C.new.awesome_method #=> Hello, world
---
選択肢3
class C
CONST = "Hello, world"
end
module M
C.class_eval(<<-CODE)
def awesome_method
CONST
end
CODE
end
p C.new.awesome_method #=> Hello, world
---
選択肢4
class C
CONST = "Hello, world"
end
module M
C.class_eval do
def awesome_method
CONST
end
end
end
p C.new.awesome_method #=> 例外が起こります
これはややこしいです。。。
まず、選択肢1は、awesome_method
メソッド内のCONSTが参照できるものがないため、例外が起こります。
参照できない理由として、CONSTを定義しているコードが同じコンテキスト内に存在しないからです。
選択肢2は、class_eval
(ブロック)で定義した場合のネストの状態、スコープを考えます。ネストの状態は、ブロックで定義されているので、class_eval
ブロックが定義されている場所がネストとなります。つまり、モジュールMです。ネストがモジュールM=定数のスコープもモジュールMであることがわかります。運よくモジュールMで定数は定義されているので、正常に文字列が出力されます。
選択肢3は、選択肢2と似ていますが、定数が定義されている場所とヒアドキュメントが使用されていることに注意が必要です。ヒアドキュメントが使用されている場合のネストは、class_eval
のレシーバです。つまり、クラスCです。ネストがクラスC=定数のスコープもクラスCです。運よく定数はクラスCに定義されているので、正常に文字列が出力されます。
選択肢4は、運が良くないですね。動きは選択肢2と同じですが、スコープ内(モジュールM内)に定数が定義されていないですね。なので、例外が起こります。
第五問
class Object
CONST = "1"
def const_succ
CONST.succ!
end
end
class Child1
const_succ
class << self
const_succ
end
end
class Child2
const_succ
def initialize
const_succ
end
end
Child1.new
Child2.new
p Object::CONST
まず、このコードたちを実行した時に、以下の順番で処理がされていきます。
- クラスの定義
- インスタンスの呼び出し
- Object::CONSTの呼び出し
まず、クラスの定義時に行われる処理を説明していきます。Objectクラスを定義する時には、定数の定義(CONST="1")とconst_succ
メソッドの定義(CONST="2")がされます。次にChild1クラスを定義する時には、最初のconst_succ
(CONST="3")が実行されます。しかし、Child1クラスの特異クラス内にあるconst_succ
が実行されません。次にChild2クラスを定義する時には、最初のconst_succ
(CONST="4")が実行されます。しかし、initialize
メソッド内のconst_succ
は実行されません。
次にインスタンスの呼び出し時に行われる処理を説明します。Child1.new
を呼び出したときには、最初のconst_succ
は呼び出されません。また、Child1クラスの特異クラス内にあるconst_succ
も呼び出されません。次に、Child2.new
を呼び出したときには、initialize
メソッド内のconst_succ
(CONST="5")が呼び出されます。
最後にObject::CONST
が呼ばれた時にはCONSTの値は"5"なので、出力される値は"5"です。
ちなみに、インスタンスの呼び出し時にconst_succ
が呼ばれない理由として、const_succ
がそれぞれのクラスメソッドとして呼び出されているからです。
落ちた原因
さて、最後に試験に落ちてしまった原因ですが、自分がRubySilverを受験したのはバージョン2.1のものです。しかし、今回受験したのはバージョン3.0でした。
テストセンターで問題を解く時、「Version3.0」という文字が目に入り、「マジか、、、やばい」と思いました。
それに試験問題は全然違かったです。
Rexをメインに使用してバージョン3.0を受験することを考えている人は一旦考えてください。
こちらの記事やこちらの記事で対策記事が書かれていますので、一度こちらをお読みください!
その他記事は以下にまとめておきます。
https://zenn.dev/universato/articles/20221011-z-rubygold3
https://ankh-systems.co.jp/blog/51
しかし、バージョン3.0の模擬問題が出ていないので対策しづらいです。。。
コツコツ地道に対策をしていきます。。。
まとめ
RubyGoldを受けようとしている人は、RubySilverよりも格段にレベルが上がると思っておいた方がいいです。
個人差はあるかもしれないですが、以下の点については十分に対策をした方が良いと思います。
- 定数のスコープ、ネスト
- ブロック
- クラスメソッドとインスタンスメソッドの違い
-
module_eval
、class_eval
、instance_eval
の違い - メソッドや定数の探索順
あと、バージョン2.1と3.0はそこそこ問題がずれているので注意をしてください!
以上