LoginSignup
2
2

Ruby Gold 3.1を受けた感想。出題傾向/対策まとめ(2023年11月)

Last updated at Posted at 2023-11-25

はじめに

2023年11月にRuby技術者認定試験Goldを受験しました。
結果は96点でした。

test

ある程度余裕をもって合格することができたので、以下を公開することにしました。

  1. 実際に出題された問題例
  2. 受験に向けて実施したこと
  3. 対策としてまとめていた点

受験に向けて実施したこと

勉強期間

Silver合格後、1ヶ月間(70H)ほど勉強しました。

  • 10月28日 Ruby技術者認定試験 Silver 受験
  • 11月23日 Ruby技術者認定試験 Gold 受験

参考にしたサイトや、使用した教材

勉強方法

問題集の解答から始めると、不明点が多すぎて、学習意欲がなくなりそうだったので、
受験者が公開しているサイトを参考にRuby Gold 3.1の出題傾向や、よく出る問題についての学習から開始することとしました。

その後、RexやRuby技術者認定試験合格教本などの模擬問題の解答を繰り返し、受験当日を迎えました。
Rexは平均90点以上取れるような状態となっておりました。

test

試験対策ポイントまとめ

私が試験学習をするにあたり、迷った点などを重点的にまとめておきます。

クラス変数

・レキシカルスコープは探索しない(現在のコンテキスト ⇒ 継承チェーンの順で探索)
・特異クラスと共通の値を保持
・特異クラス内のクラス変数の参照は特異クラスが定義されたブロックのコンテキストで行われる(自身のクラス及び継承チェーンは探索されない)

class C
  @@val = 10 #=> 探索されない
end

module M
  @@val = 20 #=> コメントアウトすると例外となる

  class << C
    p @@val #=> 20
  end
end

クラスインスタンス変数

・特異クラスとは別の値を保持

class C
  @val = 3
  class << self
    @val = 10
  end
  
  def self.val
      @val
  end
end

p C.val #⇒ 3

・サブクラスには継承されない

class B
    @val = 100
    p @val #⇒ 100
end

class C < B
    p @val #⇒ nil
end

特異クラス

・クラスの特異クラスは、自身のクラスの定数は参照できない
・インスタンスの特異クラスからは、自身のクラスの定数を参照できる

以下は例外

class A
    CONST = 'CONST'
end

class B
  class << A 
    p CONST # => NameError
  end
end

以下は例外とはならない

class A
    CONST = 'CONST'
end

$a = A.new

class B
  class << $a
    p CONST #⇒ 'CONST'
  end
end

定数

・定数の探索順は「自身のコンテキスト => レキシカルスコープ => 継承チェーン => トップレベル」の順で行われる。

・特異クラスの定数探索は定義されたブロックのコンテキストをまずは探索し、その後継承チェーンを探索する

class B
    CONST = 'B'
end

module M
  CONST = 'M' # コメントアウトするとBが出力
  b = B.new

  class << b
    p CONST #⇒ 'M'
  end
end

・モジュール内の定数参照はモジュールのコンテキストから定数を探索する(その後クラス定数は参照しない)

module M
    CONST = 'M' #=> コメントアウトすると例外
    
    def hoge
        p CONST
    end
end

class C
    CONST = 'C'
    
    include M
end

C.new.hoge

・ネストされた名前空間の定数参照について

# [コード1]
module M
    CONST = 'M'
end

module M
    class C
        p CONST #=> M
    end
end

# [コード2]
module M
    CONST = 'M'
end

module M::C
    class C
        p CONST #=> 例外
    end
end

const_getはレシーバ(self)から検索を開始する
const_defined?(:CONST, false) ⇒ 継承先を探索しない
・メソッド内の定数更新は例外が発生する。

クラス

  • superは受け取った引数をそのままスーパークラスに渡す
    • 引数を渡さない場合はsuper()とする。
class B
    def initialize
    end
end

class C < B
    def initialize(*)
        super #⇒ super()とすると例外は発生しない
    end
end

C.new('hoge') #⇒例外
  • サブクラスでinitializeが定義されていない場合、スーパークラスのinitializeが実行される。

モジュール

  • prependincludeはメソッドや定数を上書きはしない(参照を追加している)

prependはクラスの前に参照を追加

module M
end

class C
    prepend M
end

p C.ancestors #=> [M, C, Object, Kernel, BasicObject]

includeはクラスの後に参照を追加

module M
end

class C
    include M
end

p C.ancestors #=> [C, M, Object, Kernel, BasicObject]
  • prependincludeの複数指定 ⇒ 左側から読み込まれる
module M1
end

module M2
end

class C
    prepend M1, M2
end

p C.ancestors #=> [M1, M2, C, Object, Kernel, BasicObject]
  • 順番に読み込ませた場合
module M1
end

module M2
end

class C
    prepend M1
    prepend M2
end

p C.ancestors #=> [M2, M1, C, Object, Kernel, BasicObject]
  • モジュールをミックスインした後に定義したメソッドも呼び出し可能
module M
end

class C
    include M
end

module M
    def hoge
        p 'hoge'
    end
end

C.new.hoge #=> "hoge"

Date

  • date << 1 1ヶ月前
  • date >> 1 1ヶ月後
require 'date'

p Date.new(2023, 11, 11) << 1 #=> #<Date: 2023-10-11 ((2460229j,0s,0n),+0s,2299161j)>
p Date.new(2023, 11, 11) >> 1 #=> #<Date: 2023-12-11 ((2460290j,0s,0n),+0s,2299161j)>

Refinement

usingを宣言したスコープでのみ上書きする
refine C doCのインスタンスメソッドを上書きする
・クラスメソッドを上書きする場合はC.singleton_classを指定して上書きする
・メソッド内でusingを使用すると例外が発生する
using後にメソッドを再定義してもRefinementが優先される
usingを複数定義した場合は最後に宣言したusingのみ有効

Forwardable

  • extendでミックスインする
    • extend Forwardable
  • def delegator, def delegators を使用する

class_eval / module_eval / instance_eval

class_eval ⇒ クラスに対して、インスタンスメソッドを定義
instance_eval ⇒ インスタンスに対して特異メソッドを定義

・ブロックが定義されたコンテキストで定数やメソッドを探索する
・定義した定数はブロックが定義されたコンテキストで評価される(以下はTOPレベルで宣言したことになる)

mod = Module.new

mod.module_eval do
  EVAL_CONST = 100
end

p EVAL_CONST #=> 100

・文字列を渡した場合はレシーバのコンテキストで評価される

mod = Module.new

mod.module_eval(<<-EOS)
  EVAL_CONST = 100
EOS

p EVAL_CONST #=> 例外(トップレベルにはEVAL_CONSTは存在しない)

・クラスメソッドの定義する場合はself.hogeとする

・instanceに対して、module_evalで定義されたメソッドは呼び出せないので、注意

m = Module.new

CONST = "Constant in Toplevel"

_proc = Proc.new do
  CONST = "Constant in Proc"
end

m.module_eval(<<-EOS)
  CONST = "Constant in Module instance"

  def const
    CONST
  end
EOS

m.module_eval(&_proc)

p m.const #=> 例外となる

clone, dup

  • cloneは特異メソッドも含めた複製を作成
  • dupは特異メソッドは含めない
  • clonedupはシャローコピー

do endと{}の結合度の違い

do ~ endの方が結合度が高い

rescue

  • rescue Classは指定したクラスとそのサブクラスを捕捉する
    • rescue NameErrorNoMethodErrorも捕捉
  • rescueブロックでのraiseは指定したクラスではなく実際に発生したエラーをthrow
  • rescue elseは例外が発生しなかった場合のみ実行
  • rescue ensureは例外の発生有無に関わらず、必ず実行される

*args

*argsは引数がひとつでも配列として受け取る

def hoge(*args)
    p args #=> [1]
end

hoge(1)

or, and

or, and||, &&より結合度が低い。

puts true and false #=> trueが出力

freeze

・破壊的変更は不可 ※配列に指定した場合各要素の破壊的変更は可能
・変数自体の値再代入は可能

以下は例外

array = ["a", "b", "c"].freeze
array = array.map!{|content| content.succ}

p array #⇒ 例外

以下は例外

char = { :a => "A" }.freeze
 char[:a] = "B"
 p char

array = [1,2,3].freeze
array[0] = 0 #⇒ 例外

以下は例外にならない

array = [1,2,[3,4]].freeze
array[2][0] = 0

p array #⇒ [1, 2, [0, 4]]

以下は例外にならない

array = ["a", "b", "c"].freeze

array.each do |chr|
  chr.upcase!
end

p array #⇒ ["A", "B", "C"]

throw ~ catch

・戻り値は引数の二番目に指定

throw :done, [a,b,c]

RDoc

*word* ⇒ 太字
_word_ ⇒ 斜体
+word+ ⇒ タイポライター
*、 - ⇒ 番号なしリスト

*methods

methods ⇒ レシーバのpublic + protected なメソッドのみを取得
public_methods ⇒ レシーバのpublicなメソッドのみを取得
private_methods ⇒ レシーバのprivateなメソッドのみ

キーワード引数

引数が未指定の場合は例外が発生する

def hoge(foo:)
  p foo
end

hoge(1) #⇒例外

alias, alias_method

  • alias => エイリアス名 メソッド名
    • ※カンマがあるとエラー、識別子/シンボルのみ指定可能
  • alias_method => エイリアス名, メソッド名
    • ※カンマは必要。シンボル/文字列のみ指定可能

undef, remove_method

remove_methodは現在のクラスからメソッドを削除。
継承元にメソッドが定義されてあれば継承元のメソッドが呼ばれる

undef_methodはメソッド呼び出しへのレスポンスを止める。
remove_methodと違い、継承元のクラスにメソッドが定義されていてもエラーとな流。

undef後にメソッドを再定義した場合は呼び出し可能。
undef後にモジュールをincludeしても、呼び出しは不可。

Class#method_missing

class Class
  def method_missing(id, *args)
    puts "Class#method_missing" #=> ここでキャッチされる
  end
end
class A
  def method_missing(id, *args)
    puts "A#method_missing"
  end
end

A.dummy_method

const_missing

定数が見つからない時に実行
クラスメソッドとして定義する

class Object
  def self.const_missing a
    p "#{a}"
  end
end
B #⇒ 'B'

proc, yield

Procの作成
Proc.new{...}
proc {...}
lambda {...}

Procオブジェクトの呼び出し
proc.call, proc.yield, proc[]
yield ※callは付けない。

&block

&block => ブロックを明示的に受け取る
引数の最後に書かないとエラー

# 以下はエラー
def hoge(&block, *args)
  block.call(*args)
end

オプション

-t, -fはない。
-I(include)$LOAD_PATHに追加で読み込むファイルを追加
e(evaluate) ⇒ 引数で渡した文字列を評価。結果を出力
-d(debug) ⇒ デバッグモード。スレッドの例外を捕捉
-h(help) ⇒ ヘルプ
-c(check) ⇒ 文法が正しいかチェック
-w(warning) ⇒ 冗長モード
-r(require) ⇒ スクリプト実行前に指定されたファイルの実行

組み込み定数

$0 ⇒ 実行中のファイル名
$1.. ⇒ 正規表現でマッチした文字

NULLは組み込み定数ではない。

strptime / strftime

  • strptime ⇒ 文字列をTimeオブジェクトに変換
  • strftime ⇒ 日付オブジェクトを指定したフォーマットの文字列に変換

iso8601

  • Time.iso8601 ⇒ iso文字列をTimeオブジェクトに変換
  • Time#iso8601 ⇒ レシーバのTimeオブジェクトをiso8601形式の文字列に変換

classメソッド

  • Classオブジェクトに対して実行 ⇒ Classが返る
  • Classインスタンスに対して実行 ⇒ クラス名が返る

DATA, END

DATA(File obj) ⇒ __END__以降の文字列をファイルオブジェクトとして取得

===

  1. 範囲オブジェクトの範囲チェック
  2. インスタンスであるかチェック

トップレベルのメソッド

  • Objectクラスのprivateメソッドとして扱われている
def hoge
    puts 'hoge'
end

# 上記はhogeメソッドはObjectクラスのprivateメソッドとして取り扱われている
class Object
    private
        def hoge
            puts 'hoge'
        end
end

puts, print, p

  • puts, printto_sメソッドが呼ばれる
  • pinspectメソッドが呼ばれる

Module.nesting

・TOPレベルで実行すると、[]が変える
・実行結果は内側から返る

module SuperMod::BaseMod
  p Module.nesting
end
# => [SuperMod::BaseMod]

module SuperMod
    module BaseMod
        p Module.nesting
    end
end
#=> [SuperMod::BaseMod, SuperMod]

出題傾向

自分の想定よりRuby Silverで出題されるようなメソッドの利用方法などに関する問題が多かった気がします。

出題された問題

  • クラス, モジュール間の定数参照について
  • モジュールをミックスインした場合のsuperによるメソッド呼び出し
  • usingRefinementを有効にした場合のsuperによるメソッド呼び出し
  • モジュールのクラスメソッドのミックスイン
    • モジュールのクラスメソッドはincludeprependではクラスにミックスインできない。
module M
    def self.hoge
        puts 'hoge'
    end
end

class C
    include M
end

puts C.methods.include?(:hoge) #=> false
  • instance_variable_get
# どちらが正しいか
obj.instance_variable_get(:@foo) #=> こちらが正しい
obj.instance_variable_get(:foo)   
  • トップレベルに宣言したメソッドについて
  • Compartibleモジュールのミックスイン, <=>でのソート
  • Procオブジェクトの作成方法について(Proc.new {...} or lambda {...}

出題されなかった問題

  • const_eval, instance_eval
  • const_get, const_defined?
  • 特異クラス内での定数参照について
  • Enumerableモジュール、Enumerator, Enumerator::Yielder

さいごに

本記事が皆さんの合格に少しでも役立てれば幸いです🙏
モチベーション向上のため、いいねいただけると嬉しいです!!

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2