Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rubyオブジェクトモデルのクイズで最大の難問はどれか(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

参考: class BasicObject (Ruby 3.2 リファレンスマニュアル)

Rubyオブジェクトモデルのクイズで最大の難問はどれか(翻訳)

Rubyでは、あらゆるものがオブジェクトなので、Rubyのオブジェクトモデルをしっかり理解しておくことで有能な開発者になれます。数ヶ月前に公開したクイズでは、Rubyオブジェクトモデルの知識を問う問題を出題しました。この短い記事では、このクイズの回答に関する平均的な成績を確認し、どの問題が最も難しかったかを特定してみることにします。

はじめに

私たちは数か月前にRubyオブジェクトモデルのクイズを公開しました。問題は全部で10問で、Rubyの基本的な構成要素であるオブジェクトモデルに関する知識を自己診断できます。クイズでは、モジュール、継承、祖先クラスのチェイン、シングルトンクラスなどの概念も扱っています。

ネタバレ注意: まだクイズを解いていない方は、続きを読む前にぜひ一度問題を解いておきましょう

全回答者のデータを集計して、どんな結果になったか、どの問題が最も難易度が高かったかといったことがわかったので、振り返ってみることにします。

得点別の内訳

このクイズは、本記事執筆時点で615回の試行が登録されており、平均スコアは34%でした。同じ人物が複数回登録した可能性もありますが、おそらく結論にはさほど影響しないと考えています。以下のグラフは、回答者の得点分布図です。

「Rubyオブジェクトモデル」クイズの全回答者の得点分布

平均34%という低い値となっていますが、問題の難易度は意図的に高くしており、Objectの先祖チェインの内部構造に踏み込んだものにしてありました。こういう詳細部分は、多くの場合Ruby言語で開発するときに見えないようになっています。

各問に制限時間を設定してあったことも、採点に大きく影響したのは間違いありません。1問あたりの制限時間は45秒で、最後の2問についてはもう少し長く60秒としました。これによって、回答者が制限時間に追われて考える時間が足りず、焦って不正解を選択してしまった人もいたのは確実です。

時間制限は一般にはよいものであると私は考えます。クイズ回答者に短時間で最後までクイズを解かせるようにし、回答者が考える時間を厳しく制限できます。
最も重要なのは、各問に制限時間が設定されていることで、回答者が問題をコンソールにコピペして結果をチェックするのが困難になる点です(コンソールを使うのは本クイズの趣旨に反していると思います)。

しかし今にして思えば、制限時間をもう少しだけ緩めて回答者により公平な機会を与えられたかもしれませんね。

採点結果の最頻値1は30-39で、点数が70%を超えた人は非常にまれでした。70%超えの方に惜しみない賞賛を贈りたいと思います!

問題別の内訳

次に、個別の問題についてユーザーがどう回答したかを見てみましょう。以下のグラフのバーをクリックすると、その問題のスクリーンショットが表示されます(訳注: 動的な操作は元記事で行ってください)。

最も難易度の高かった問題

最も正答率が低かったのは問6であることがわかります。正答率はわずか5.16%でした。この問題は、Objectクラスを調べて、どのメソッドがBasicObject由来であるかを答えるものです。

「Rubyオブジェクトモデル」の最難問

BasicObjectは祖先クラスへのチェインの最上位にあります。Objectクラスはこのベースクラスを継承しますが、さらにKernelモジュールも継承しています。すなわち、この問いの趣旨は、このリストどのメソッドがBasicObject由来で、どのメソッドがKernelモジュールから取り込まれたものかというものです。その答えはirbコンソールですぐにわかります。

>> Object.ancestors
=> [Object, Kernel, BasicObject]
>> BasicObject.instance_methods
=> [:!, :equal?, :__send__, :__id__, :==, :!=, :instance_eval, :instance_exec]

上のようにBasicObjectクラスからインスタンスメソッドを取り出してみると、わずか8個です。これらはかなり基本的なメソッドですが、日常的に使うものではありません。

__id__==!=はオブジェクトのidを扱いますが、お馴染みのobject_id は、実はKernelモジュールによって導入されたものです。

同様に、BasicObjectのインターフェイスには、メソッド呼び出しの構成要素(とされるもの)である__send__instance_evalinstance_execもあることがわかります。しかし:send:public_send:tapといった便利なメソッドは、これもKernelモジュール由来なのです。

つまり、問6でリストアップされているメソッドのうち、本当にBasicObjectから継承されたものは#equal?#!=だけというのが答えです。この問題は難易度が高く、多くの回答者が正答できなかったのも無理はありません。

正しく理解できたことを確かめるために、先祖クラスへのチェインにある各コンポーネントが公開しているインスタンスメソッドの個数を調べてみましょう。

puts "BasicObject"
puts "All instance methods: #{BasicObject.instance_methods(true).size}"
puts "Own instance methods: #{BasicObject.instance_methods(false).size}"

puts "Kernel"
puts "All instance methods: #{Kernel.instance_methods(true).size}"
puts "Own instance methods: #{Kernel.instance_methods(false).size}"

puts "Object"
puts "All instance methods: #{Object.instance_methods(true).size}"
puts "Own instance methods: #{Object.instance_methods(false).size}"

上のスクリプトの実行結果は以下のようになります2

BasicObject
All instance methods: 8
Own instance methods: 8
Kernel
All instance methods: 43
Own instance methods: 43
Object
All instance methods: 51
Own instance methods: 0

Objectのインターフェイスにはインスタンスメソッドが全部で51個あり、うち8個はBasicObjectクラスが由来で、残りの43個はKernelモジュールから導入されたものであることがわかります。

まとめ

本記事では、先ごろ公開した「Rubyオブジェクトモデル」クイズに登録された結果を簡単に分析した結果をお見せしました。平均点が低いことから、難易度がやや高すぎたことと、ほとんどのRuby開発者が普段体験していないような問題が多かったと思われます。また、回答の時間制限がここまで厳しくなければスコアはもっとよかった可能性があるという仮説も立てています。

特定の問題の成績については、問6の成績が最も低かったことが記録されています。この問題は、特定のメソッドがBasicObjectクラスとKernelモジュールのどちらに存在するかを問うもので、多くのRubyistが普段の開発で気にする必要のない詳細実装です。すなわち、この問題は、他の多くの問題と同様に、実力を測定するというよりは実装上のトリビアな雑学と考えるのがよいでしょう。

関連記事

Rails: Active Recordモデルのスレッド安全性問題をインスタンス変数で解決する(翻訳)

Ruby: 演算子の優先順位でハマった話(翻訳)


  1. 最頻値 - Wikipedia -- モード 
  2. 訳注: 記事にはRubyバージョンが書かれていませんが、Ruby 3.2.1で記事と同じ結果になることを確認しました。他のRubyバージョンでは個数が若干異なる可能性があります。また、Ruby 3.2.1でもirbでObject.instance_methods(true).sizeなどを実行すると個数が56個に増加するのでご注意ください。 

CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。