WED Engineering Team Blog

RubyKaigi 2024 アキネイター🧞クイズ by WED

Akinori Musha (@knu)

WEDの武者(@knuExternal link icon)です。RubyKaigi 2024External link iconWEDExternal link iconブースの右半分では、Rubyの組み込みメソッドを当てる「アキネイターExternal link icon🧞」クイズを出題しました。

BoothExternal link icon

そういえば、私の名前(Akinori)にちなんだんですか、と聞かれたりしましたが、偶然です。😄

ゲームの概要

ここ数年、TikTokやYouTube Shortsで流行っている遊びなのでご存じの方も多いと思いますが、いわゆる「20の質問External link icon」のような形式のゲームです。

AkinatorExternal link icon

出題者が想定した答え(本家だと有名人の名前)を回答者が当てるクイズの一種ですが、特徴的なのは挑戦者が出題者に対して質問をすることです。挑戦者は、Yes/Noで答えられる質問を出題者に次々と投げかけていき、Yes/Noの回答から候補を絞っていき、最終的に想定解をずばり当てることができればクリアとなります。

Yes/Noの情報しか得られないというのがおもしろいところで、今回のお題であるRubyの組み込みメソッドの場合、

「引数はいくつ取れますか?」

と数を聞くことはできませんが、

「引数の数は決まっていますか?」
「引数は2個以下ですか?」

のようにして絞り込むことができます。

今回は質問数に制限なしとしたので、

「メソッド名は a で始まりますか?」
「メソッド名は b で始まりますか?」

と繰り返して頭文字を特定し、

「メソッド名の2文字目は a ですか?」

と2文字目以降も順次確定していけば機械的に解けるわけですが、そこはRubyKaigiというテックカンファレンスの場ですので、みなさん手数を減らすべく、

「メソッド名は a から m の間で始まりますか?」
「いいえ」
「メソッド名は n から t の間で始まりますか?」
「はい」

のように二分探索を意識したり、

「文字列を返しますか?」
「破壊的メソッドですか?」

とメソッドの挙動から絞り込むなど、工夫して楽しんでいただきました。

Day 1

初日のお題は、知らない人もいるだろうけど、ぜひ知っていざというときに活用してほしいメソッドを選びました。

Q&A

質問回答
引数を取りますか?はい
引数を複数取りますか?はい
無引数でも使えますか?はい
ブロックは取りますか?いいえ
Numericの派生クラスに属しますか?いいえ
特異メソッドですか?はい
メソッド名は t から z で始まりますか?はい
R から始まるクラスまたはモジュールに属しますか?はい

解答編

答えは…

Regexp.union でした!

解説

ドキュメントはこちら: 英語版External link icon / 日本語版External link icon

引数として任意の数のパターン(RegexpまたはStringのリスト or 配列)を受け取り、そのいずれかにマッチするRegexpオブジェクトを返します。

words = %w[WED ONE Zero]
Regexp.union(words)
#=> /WED|ONE|Zero/

Regexp.union(/\d+/, /[a-z_]\w+/i)
#=> /(?-mix:\d+)|(?i-mx:[a-z_]\w+)/
# 正規表現オプションも考慮されます。

Regexp.union("Ruby", "Rust")
#=> /Ruby|Rust/
# /Ru(?:by|st)/ のような最適化はしてくれません。

# 何も渡さないと、「何にもマッチしない正規表現」が返ります。
Regexp.union()
#=> /(?!)/
Copy to clipboard

Ruby 1.8.1で追加されました。1.8.7から、配列も受け付けるようになりました。

ユースケースは確かに存在しますが、手で実装しようとすると、文字列の場合だけを考えても以下のように意外と煩雑です。ここで Regexp.escape を忘れると、 .+ があるときに意図せぬ挙動をしてしまいますし、信頼できない入力から生成するときは脆弱性にもつながります。

Regexp.new(words.map { |w| Regexp.escape(w) }.join("|"))
Copy to clipboard

他の言語には同等機能が見られませんが、Stack Overflowなどでは質問例があり、愚直に実装するよう回答されていました。

そうしたかゆいところに手が届くような機能を標準で用意しているところが、実にRubyらしいと思います。 Rubyの中ではもちろん、他の言語・ライブラリにも広まってほしいと思い、Day 1のお題にしました。

Day 2

二日目は、少しクセ球を投じてみました。

Q&A

質問回答
文字列を引数に取りますか?はい
数値を引数に取りますか?はい
無引数でも使えますか?はい(でもそういう使い方はしないかも)
ブロックは取りますか?いいえ
Ruby 2.0に存在しましたか?いいえ
特異メソッドですか?いいえ
メソッド名は m から s で始まりますか?はい
K から始まるクラスまたはモジュールに属しますか?はい
標準出力に何か出力しますか?はい

解答編

答えは…

Kernel#pp でした!

解説

ドキュメントはこちら: 英語版External link icon / 日本語版External link icon

p の高機能版です。要素の多い配列やハッシュオブジェクトも行折り返しやインデントを使って読みやすく表示してくれます。

Ruby 2.5から、 require "pp" しないでも使えるようになりました。

勘のいい人は、「requireしないと使えないものは含まない」からメタ読みしたかも?

IRBやrails consoleでも pp 形式の出力がサポートされています。 インタラクティブなセッションで、大きなデータ構造をきれいに見られるのは pp のおかげですね。

たとえばですが、ボードゲームの盤面、木構造のデータなどを現すクラスに pretty_inspect を実装してやると、 pp するだけできれいに二次元表示できるようになり、開発の助けになることでしょう。

Day 1の Regexp.union に続き、Day 2の pp (prettyprint)も田中哲(akr)さんの作品です!

Day 3

三日目は、最終日ということでちょっといじわるな問題でした。

Q&A

質問回答
文字列を引数に取りますか?いいえ
引数を取りますか?いいえ
ブロックは取りますか?はい
メソッド名は a から t で始まりますか?いいえ
Ruby 2.0に存在しましたか?いいえ
Ruby 2.5に存在しましたか?はい
仕事で使いますか?おそらくいいえ
別名がありますか?はい

解答編

答えは…

Kernel#yield_self でした!

解説

ドキュメントはこちら: 英語版External link icon / 日本語版External link icon

文字通り yield self してその値を返すメソッドです。ブロックを渡さない場合の特殊処理を除くと、文字通りこういうメソッドです。

module Kernel
  def yield_self
    yield self
  end
end
Copy to clipboard

オブジェクトに対する加工をメソッドチェーンで書きたいときに使います。

article.title.yield_self { |t| format("「%s」", title) }.to_json
Copy to clipboard

機能要望と賛同の声は強かったもののいい名前が見つからず、さんざん大喜利が続きました。

https://bugs.ruby-lang.org/issues/6721External link icon

そこで「いい名前」を棚上げし、愚直な名前を採用してRuby 2.5に登場しました。

まもなく、 then という名前が提案され、「JavaScriptのPromiseの語彙とぶつかる」という懸念もあったものの、Matzの一声で Ruby 2.6 に別名として追加されました。

https://bugs.ruby-lang.org/issues/14594External link icon

今ではみんな新しい名前の then しか使っておらず、忘れ去られつつある yield_self はRuby一不遇なメソッドと言えるかもしれません。

ところで、このメソッドは実際は Kernel モジュールに定義されているのですが、日本語版リファレンスマニュアルでは Object クラス内に立項されていました。 そこで、検索しながら答える挑戦者のために、「Object クラスに属しますか?」には「はい」と答えるなどの配慮が必要なのがちょっとした誤算でした。😝

終えての感想

今回は、質問数の上限や時間制限を設けず、緩めのルールで楽しんでいただきました。そして、この形式のゲームに慣れていない方のために、質問例をいろいろと例示してヒントとしてお出ししました。

しかし、それらの例を言われるがままに採用する方はほとんどおらず、渾身の一問を厳選したり、いい質問を自分でひねり出すべく長考に入る姿が多く見られたのがとても印象的でした。そこには、Rubyistのみなさんの「自分で問題を解きたい」「かっこよく解きたい」という魂を感じた次第です。

今回の企画のアキネイターは、問題を用意すること自体は簡単なのですが、解き終えた挑戦者の方に何がしか持ち帰ってもらうべくメソッドの解説やバックストーリーをスライドにしたり、きわどい質問にきちんと対応できるよう想定問答集を準備したり、それでも飛んできた想定外の質問についてスタッフ間で情報共有したりといった部分には手間をかけました。

さて、3日間にわたる長丁場で、特に我々に鮮烈なインパクトを残してくれたのはこんな方々です。

  • 1人1問、計たったの3問で正答を導き出してしまったグループ
  • 矢継ぎ早の質問で1分もかけずに正解に辿り着いてしまった挑戦者
  • 「返り値の型は self? ですか?」と鋭角な質問を下さったRBSExternal link icon & SteepExternal link iconの作者様
  • 初日の早い時間から来ていただき、20分あまりもの格闘の挙げ句、答えのメソッドを覚えていなかったRubyのパパ
  • 毎日欠かさずRubyのすべてのコミットに目を通していながらも、PCを開いてIRBからメタプログラミングでメソッドを絞り込むという大技を繰り出してくださった ruby trunk changesExternal link icon の運営者様

これに限らず、すべての対戦にそれぞれドラマがあり、出題者側も大いに楽しませていただきました。

訪れてくださったすべての方々に感謝いたします。みなさん本当にお疲れ様でした!