CTC 教育サービス
[IT研修]注目キーワード Python UiPath(RPA) 最新技術動向 Microsoft Azure Docker Kubernetes
こんにちは。 ゼネットの藺藤です。
今回は「Ruby技術者認定試験・ゴールド試験合格方法」というテーマでお送りします。 前回のコラムの続きとなる内容です。(前回のコラムをご覧になってない方はこちらも一読ください!)
それでは始めましょう!
さて、前回のコラムではRuby技術者認定試験の概要と、特にシルバー試験について私や同僚が実践しているお勧め勉強法をご紹介しました。 今回はその続編として「ゴールド試験」合格のためのポイントを解説します。
「Rubyアソシエーション認定プログラマ・ゴールド」は、シルバー試験で一通りの文法、ライブラリを覚えた受験者に対して、更に深く・広く文法を理解した上で適切なプログラム設計を行えることを目指します。 シルバー試験と比べて、ゴールド試験はRubyの考え方や概念をしっかり理解することの重要性がより高い試験となります。
今回は「ゴールド編①」として、Rubyのオブジェクト指向について説明していきます。 本コラムでは、具体的かつ網羅的な説明を行うことは目指しません。 それらは他のテキストをご覧になった方がずっと参考になるでしょう。 その代わりに、読者の方が自分自身で理解してゆく一助となることを目的とします。 このため、実際にプログラムを実行し、確認しながら読み進めていってください。
初めに、簡単に「Ruby Association Certified Ruby Programmer Gold version 2.1」の概要をおさらいしておきます。(*1)
Ruby 2.1 を対象としています。
試験はCBT方式で行います。
シルバー試験と同様に、ゴールド試験では50問中38問以上正解することで合格となります。 試験結果は受験後すぐにわかります。
本試験は以下のような方にお勧めします。
特に、ゴールド試験では出題される内容がやや高度ですので、既にシルバー試験を合格されて、更なるレベルアップを図りたい方が主たる対象かと思われます。
なお、「Ruby Association Certified Ruby Programmer Gold version 2.1」として認定されるためには、シルバー試験及びゴールド試験の両方に合格することが必要です。
出題範囲は以下のとおりです。 ゴールド試験で新たに出題範囲に追加された項目は赤字で示します。 また、Ruby 1.9から2.1の間で追加された比較的新しい項目については(+)を付しておきます。 シルバー試験の範囲と重なる項目であっても、油断せずに復習してください。
[実行環境]
・コマンドラインオプション ・組み込み定数/変数
[文法]
・変数と定数 ・演算子 ・ブロック
・例外処理 ・大域脱出 ・キーワード引数 (+)
・ラムダ式(->)(+)
[オブジェクト指向]
・メソッドの詳細 ・メソッドの可視性 ・クラスの詳細
・クラスの継承 ・モジュールの詳細 ・Module#prepend (+)
・Refinements (+)
[組み込みライブラリ]
・よく使用されるクラス(Object, Kernel, Module等)
・よく使用されるモジュール(Enumerable, Comparable等)
・数値
・正規表現
・Proc
・Thread/Fiber (+)
[標準添付ライブラリ]
・よく使用されるライブラリ(socket, date, stringio等)
本試験を受験する方はRuby 2.1をインストールし、その挙動を確認しながら試験範囲を学習することが必須だと思われます。 出題範囲をご覧になっていただくと分かり易いですが、比較的最近のバージョンで追加された項目(ラムダ式、キーワード引数, prepend, Refinement等)に関しても少なくない割合で出題されます。 新規追加された項目は、その後のバージョンアップで細かな挙動が変更されることも少なくありません。 学習の際にはRuby 2.1を利用することを強くお勧めします。
本試験に合格するためには、Rubyにおけるオブジェクト指向についてしっかり学習する必要があります。 クラスやモジュール、継承・include・prependの概念、Rubyにおける定数とは何なのか、また、メソッドの可視性とはどのようなものなのか、これらの点を理解する必要があります。 Rubyというプログラミング言語の根本とも言えるものですので、十分な時間をかけて学習しないとなかなか"身についた"と言えるレベルには到達できないと思います。 「ゴールド試験」の範囲では最も理解の難しい分野ですが、ひとたび理解すると全体の見通しが非常にクリアになる分野でもあります。
ライブラリに関しては、一つ一つのライブラリの利用方法を学習することはそれほど大変ではありませんが、とにかく数が多いので計画的に学習する必要があります。 標準ライブラリを学習してゆくと、Rubyの(そしてライブラリの)根底に流れる"統一感"といったものを感じることができ、おもしろいのではないでしょうか。 何よりも、実際にこれらのライブラリを使いこなせるとRubyを使ってできることが格段に増えますので、じっくりと取り組んでほしい分野です。
これまで受験者から聞いた限り、試験時間が不足する心配はあまり無いようです。 実際の試験の合否を左右するのは、受験前にどれだけ時間をかけて学習し、Rubyの考え方をきちんと理解しているのかにかかっているようです。
ここからは学習方法について説明してゆきます。 シルバー試験とゴールド試験では出題範囲が異なります。 シルバー試験対策については前回のコラムをご覧になってください。 ゴールド試験合格に向けた学習を効率よく行うためには以下をお勧めします。
テキストとしてまず挙げたいのは「Ruby技術者認定試験合格教本 Silver/Gold対応 Ruby公式資格教科書」です。(*2) ゴールド試験で必要な範囲が明記されていますので、こちらを参考に学習してください。 この本ではRubyの文法やオブジェクト指向の理解、標準ライブラリの理解に必要な内容がすべて網羅されています。 特に、標準ライブラリに関しては膨大なライブラリの中から試験対策として必要になる部分を絞って説明されているので受験者の大きな助けになることでしょう。
もう一つ、試験勉強の一環でぜひ読んでいただきたい本をご紹介します。 「メタプログラミングRuby」です。(*3) この本はRubyの入門書ではなく、読者に要求されるレベルとしてはシルバー試験に合格していること(または合格するのに十分な実力を持っていること)が一応の目安になるかと思います。 Rubyにおけるオブジェクト指向の理解を助ける豊富な説明があるため、この本を読んでおけばゴールド試験対策としても効果的です。 メソッドの仕組み、継承の仕組み、Rubyの仕組みを理解する上でも多いに参考になりますので、Rubyをプログラミング言語として使う側の人間であれば一度は読むべき本です。 タイトルにもなっている「メタプログラミング」は、プログラミングのテーマとしては少々アドバンストな内容です。 メタプログラミングを知らなくてもプログラムを作成することは可能ですが、知っておくとプログラミングの柔軟性が飛躍的に向上しますので、そういった意味でもぜひ読破することをお勧めします。
机上の学習と平行して進めてもらいたいものが、やはり実際に実行環境を使ってRubyを動かしてみることです。 特にゴールド試験の範囲では、理解が曖昧なままだと思わず間違った解答をしてしまうような問題も出題されるため、実際に自分の手で動作させながら理解してゆく学習が効果的だと思います。
実行環境はRuby 2.1.x 系の最新のものをインストールしてください。 Windows 環境の場合はRubyInstaller (*4)を使うと手軽に導入できます。
ゴールド試験をシルバー試験と比べた場合、暗記的な内容も大事ですが、Rubyの仕組みを理解することがより重要になってきます。 このため、テキストを一回軽く通読した後に問題演習に取り組むと効果的です。
練習問題としては、先にテキストとして挙げた「Ruby公式資格教科書」に付属する問題がよいと思います。 試験1回分(50問)の問題がありますので、学習がある程度進んだらチャレンジしてください。 各問題に対して丁寧な解説がついていますので、問題と解説をセットで理解するように努めてください。 問題を一度だけ解いて終わりとしてしまうのはもったいないので、前回ご紹介 した「○△×法」を活用して試験当日までに100%に仕上げていきましょう。 試験本番で確実に一発合格するためにも、練習問題は正解率が9割を越えるまで繰り返してください。
学習方法としては次の順序をお勧めします。
テキストを読み込み、試験で問われる内容をインプットすること、また、実行環境を使って正しく理解できているのかアウトプットしながら確認するといったことが重要かと思います。
私なりの学習のコツですが、テキストやブログ等に載っているコード例に対して、"元々の設定とは少しだけ違う状況を自分で設定し、どのように動作するのか"を確認してみてください。 例えば、『String#splitの説明を見かけたら、コード例にないパターンを自分で確かめてみる』といった具合です。
# 例えばString#splitの以下のようなコードを見かけたら... "text,text,abc".split(",") #=> ["text", "text", "abc"] # 次のような場合も試してみよう! "text,text,abc".split("text") # 結果はどうなるでしょう? "text,text,abc".split("") # 結果は? "text,text,abc".split(/./) # 結果は??
このような学習法ならば、ちょっとした時間の積み重ねで取り組むことができますよね。
ここから先では、読者の方にご自身で確かめていただきたい"宿題"を出します。 お手元の環境で実行してみてください。
ゴールド試験では「Rubyにおけるオブジェクト指向の理解」が重要になります。 他のオブジェクト指向言語を既に習得されている方も、Ruby特有の部分もありますのでしっかりと理解を深めておきましょう。 ゴールド試験の出題範囲を全て本コラムで解説することは難しいので、今回は特にオブジェクト指向を取り上げて説明したいと思います。
・便利なメソッド
初めに、オブジェクト指向の理解を助ける便利なメソッドを紹介します。
① #class
② .new
初めに#classと.newを確認します。 まずは次のコードをご覧になってください。
# オブジェクトのクラスを返すメソッド "text".class #=> String # クラスからインスタンスを生成するメソッド Time.new(2016, 1, 1) #=> 2016-01-01 00:00:00 +0900 # 「クラス」と「インスタンス」を行ったり来たりしてみる例 Time.new(2016, 1, 1).class.now #=> 2016-01-08 10:53:05 +0900
クラスに対して.newメソッドを呼ぶことで、そのクラスのインスタンスを作成することができます。 一方、インスタンスが属するクラスを取得するには#classメソッドを利用します。 例のように.newおよび#classを使って、クラスとインスタンスを行き来することも可能です。 さて、ここで読者の方に一つ宿題を出したいと思います。
【宿題】インスタンスが属するクラスを取得するには#classメソッドを利用します。 では、クラス(例えばString)に対して#classメソッドを実行すると結果はどうなりますか? 更に、String.classを呼び出した戻り値に対して.classを呼び出すことはできますか? もし呼び出せるならば結果はどのようになりますか?
③ .superclass
④ .ancestors
Rubyでは「クラスの継承」が可能です。 クラスAを継承したクラスBを作成した例を示します。 継承元のクラスのことを「親クラス」または「スーパークラス」と呼びます。 メソッド.superclassはその名の通り親クラスを返すものです。 .superclassメソッドの実行例を示します。
# 親クラスAを作成する class A ; end # Aを継承した子クラスBを作成する class B < A ; end # .superclassは親クラスを返すメソッド B.superclass #=> A # Bクラスの親クラスはAクラスである。 A.superclass #=> Object # Aクラスの親クラスはObjectクラスである。
classキーワードを使って定義されたクラスは、明示的に継承元クラスを指定した場合には継承元クラスの子クラスとなります。(BクラスはAクラスの子クラスです。) 一方、継承元クラスを指定しなかった場合には暗黙的にObjectクラスの子クラスとなります。
.superclassメソッドは親クラスを返しますが、.superclassを何度も繰り返し呼び出すことで継承関係を辿ってゆくことができます。
上の例ではB.superclassを呼び出した結果はAとなり、更にA.superclassを呼び出すと戻り値はObjectになります。 では、Objectクラスの親クラスは何クラスでしょうか?
答えは BasicObjectクラスになります。 BasicObjectクラスはRuby 1.9で導入されました。 普段のプログラミングではObjectやBasicObjectを意識する機会はあまり多くないかもしれません。 ただ、このような継承関係があることはしっかりと覚えておきましょう。
【宿題】BasicObjectクラスの親クラスは何でしょうか? BasicObject.superclass を実行して確かめてください。
ここで.ancestorsメソッドを紹介します。 このメソッドはクラスの継承関係を配列で返すメソッドです。 実際に先ほど作成したBクラスに対して.ancestorsメソッドを呼び出してみましょう。
class A ; end class B < A ; end B.ancestors #=> [B, A, Object, Kernel, BasicObject]
側に向かって順番に並んでいる配列です。 先ほど.superclassメソッドを使って確かめた通り、Bの親クラスはA, Aの親クラスはObjectとなりますが、ObjectとBasicObjectの間にKernelなるものが存在しています。 これは「Kernelモジュール」と呼ばれるものです。
まとめると、.ancestorsはクラスの継承関係を配列で返すが、実際にはこの配列にはクラスに加えてモジュールも含まれます。 継承関係としてKernelはObjectよりも"更に親側"に存在することにも注意してください。 配列の要素であるクラスやモジュールの順番は重要な意味を持ちます。
Rubyでは親クラスが持つメソッドを子クラスでオーバーライドすることができます。 メソッドをオーバーライドしたときの挙動は.ancestorsの戻り値を使って説明することもできます。 あるメソッドが呼び出された時、そのメソッドが定義されているのはどのクラス(またはモジュール)なのかを見つけるために、Rubyは.ancestorsの戻り値の配列を子供側から順に探していきます。 そして、目的のメソッドを持つクラスまたはモジュールを発見したら、それ以上は親側を探索せずに見つけたメソッドを実行します。 継承の順序が重要となるのはこのためです。
⑤ #instance_of?
⑥ #is_a? および #kind_of?
ここに挙げた3つのメソッドは「クラスとインスタンスの関係性」を調べるためのものです。 実際に例を見てみましょう。
class A ; end class B < A ; end a = A.new b = B.new [a.instance_of? A, a.is_a? A, a.kind_of? A] #=> [true, true, true] [b.instance_of? A, b.is_a? A, b.kind_of? A] #=> [false, true, true] [7.instance_of? A, 7.is_a? A, 7.kind_of? A] #=> [false, false, false]
#instance_of?メソッドは、レシーバが引数に与えたクラスのインスタンスであるかどうか判定して結果を返します。 例の場合では、aはAのインスタンスなのでtrueを返します。一方、bや7はAのインスタンスではないため、falseとなります。
#is_a?は#instance_of?よりも緩い条件判断となり、
・レシーバが引数で与えたクラスのインスタンスである
・レシーバが引数で与えたクラスの子クラスのインスタンスである
のいずれかであればtrueを返します。
bは、Aの子クラスBのインスタンスなので b.is_a? A はtrueです。 一方、Fixnumクラスのインスタンスである7の場合には、クラスAやBと継承関係を持たないためfalseとなります。 なお、#kind_of?は#is_a?の別名です。
ここまで紹介してきたメソッドは単にお勉強用のメソッドというわけではなく、実際のプログラム中でも使用することがありますので、覚えておいて損はないでしょう。 以下の説明でよくわからない部分があったら、紹介した6つのメソッドを使って調べてみましょう。
・クラスの継承とMix-in
さて、ここからは実際にクラスやモジュールについて例題を基に考えてゆきます。
【例題1】
次のコードの実行結果はどうなるでしょうか。
module M def hoge ; puts "m" ; end end class A def hoge ; puts "a" ; end end class B < A include M def hoge ; puts "b" ; end end B.new.hoge # 戻り値は?
クラスを継承したり、モジュールをincludeしたりと、クラスBの定義は少々複雑に見えるでしょうか。 Bクラスのインスタンスを生成し、#hogeメソッドを呼び出した場合の戻り値はどうなるか考えてゆきます。
まず、クラスBはクラスAを継承しています。 この時点では次のような継承関係がわかります。
A.ancestors #=> [A, Object, Kernel, BasicObject] #(AはBの親なので...) B.ancestors #=> (少なくとも)[B, A, Object, Kernel, BasicObject]
クラスBは更にモジュールMをincludeしています。 モジュールをincludeしたときは、「includeされたモジュールは、クラスとそのスーパークラスの間に追加される」と覚えてください。 今の場合ではBとObject(Bの親クラス)の間にMが追加されます。 ですので、最終的には以下のような継承関係になります。
# (BはMをincludeしているので...) B.ancestors #=> [B, M, A, Object, Kernel, BasicObject]
メソッド呼び出しのルールは継承関係の子供側から順にメソッドを探してゆくので、現在の状況ではA, B, Mの全てがメソッド#hogeを持つが、実際に呼び出されるのはB#hogeです。
モジュールの利用法として、
・include
・prepend
・extend
の3つが挙げられます。 上で見たように、クラスにモジュールをincludeした場合の継承関係を考えると、モジュールは現在のクラスと親クラスの間(継承の"上側")に追加されます。 一方prependした場合は、モジュールは現在のクラスの"下"に追加されます。
【例題2】
次のコードをみてください。
module M1 ; end module M2 ; end module M3 ; end module M4 ; end class A1 prepend M1 prepend M2 include M3 include M4 end A1.ancestors # 戻り値は?
この場合の継承関係はどうなるでしょうか?
まず、クラスA1はモジュールM1をprependしています。 prependの場合、継承関係においてモジュールはクラスよりも子供側に追加されます。
#(M1をprependすると...) A1.ancestors #=> [M1, A1, Object, Kernel, BasicObject]
続いてモジュールM2をprependしています。 この場合は「M1よりも更に子供側にモジュールが追加」されます。 考え方としては、Aクラスを継承したM1という"クラス"があると思い、それに対してprependすると思って下さい。
#(M1をprependすると...) A1.ancestors #=> [M2, M1, A1, Object, Kernel, BasicObject]
更にM3をincludeしています。 includeする場合は親クラスに注意してください。 現在の場合、A1とObjectの間に追加されます。
#(M3をincludeすると...) A1.ancestors #=> [M2, M1, A1, M3, Object, Kernel, BasicObject]
最後にM4をincludeすると以下のようになります。 モジュールM4は、A1クラスとその親のM3"クラス"の間に追加されます。
#(M4をincludeすると最終的な解答になる) A1.ancestors #=> [M2, M1, A, M4, M3, Object, Kernel, BasicObject]
includeやprependの結果がどのようになるのかという問題について、今回は"モジュールを一種のクラスとみなす"ことで説明しました。 もちろん、厳密にはクラスとモジュールは別物ですが、このように考えることでもクラスの継承、モジュールのMix-inを理解できます。 気になる方は「特異クラス」や「特異メソッド」といったキーワードを調べてみてください。
【宿題】extendについて、その振る舞いを調べてみましょう。
【宿題】複数のクラスやモジュールが継承・Mix-inしている例を考え、ここまでの学習内容が理解できているのか確認してみましょう。
今回は「Ruby技術者認定試験・ゴールド試験合格方法」というテーマでお話ししました。
本文中でも述べましたが、irb等を利用して実際に自分で手を動かしながら理解してもらえるとよいと思います。
次回はオブジェクト指向以外の分野についてお話しします。 お楽しみに。
それでは、Enjoy Ruby!
*1 :「Ruby技術者認定試験制度」について詳しくはこちらを参照してください。
http://www.ruby.or.jp/ja/certification/examination/
*2 : 増井 雄一郎他著「Ruby技術者認定試験合格教本 Silver/Gold対応 Ruby公式資格教科書」技術評論社(2015)
*3 : Paolo Perrotta著、 角 征典訳「メタプログラミングRuby 第2版」オライリージャパン(2015)
*4 : 「RubyInstaller」 http://rubyinstaller.org/
*5 : ゴールド試験の学習であれば、初めのうちはirbの利用をお勧めします。 クラスの継承やモジュールのMix-inに関する知識を得たらpryを利用してみましょう。(pry上で.ancestorメソッドを実行してみてください。 きっと発見があるはずです。)
[IT研修]注目キーワード Python UiPath(RPA) 最新技術動向 Microsoft Azure Docker Kubernetes