Skip to content

Commit dfea53d

Browse files
authored
Merge pull request #77 from wedinc/rubykaigi-2024-akinator
Add an article about RubyKaigi 2024 Akinator Quiz
2 parents 0fece3e + c275143 commit dfea53d

File tree

4 files changed

+276
-2
lines changed

4 files changed

+276
-2
lines changed
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
---
2+
title: "RubyKaigi 2024 アキネーター🧞クイズ by WED"
3+
author: knu
4+
tags: Conference, Event, Ruby, Quiz
5+
Published At: May 31, 2024
6+
date: 2024-05-31
7+
image: 202405-rubykaigi-2024-akinator.png
8+
---
9+
10+
<style>
11+
img[alt="Akinator"] {
12+
width: 480px;
13+
max-width: 100%;
14+
}
15+
table th:first-of-type {
16+
width: 400px;
17+
}
18+
</style>
19+
20+
WEDの武者([@knu](https://github.com/knu))です。
21+
22+
[RubyKaigi 2024](https://rubykaigi.org/2024/)[WED](https://rubykaigi.org/2024/sponsors/#sponsor-577)ブースの右半分では、Rubyの組み込みメソッドを当てる「[アキネイター](https://jp.akinator.com/)🧞」クイズを出題しました。
23+
24+
[![Akinator](/content/202405-rubykaigi-2024-akinator/Akinator.png)](/content/202405-rubykaigi-2024-akinator/Akinator.png)
25+
26+
そういえば、私の名前(Akinori)にちなんだんですか、と聞かれたりしましたが、偶然です。😄
27+
28+
## ゲームの概要
29+
30+
ここ数年、TikTokやYouTube Shortsで流行っている遊びなのでご存じの方も多いと思いますが、いわゆる「[20の質問](https://ja.wikipedia.org/wiki/%E4%BA%8C%E5%8D%81%E3%81%AE%E8%B3%AA%E5%95%8F)」のような形式のゲームです。
31+
32+
出題者が想定した答え(本家だと有名人の名前)を回答者が当てるクイズの一種ですが、特徴的なのは挑戦者が出題者に対して質問をすることです。挑戦者は、Yes/Noで答えられる質問を出題者に次々と投げかけていき、Yes/Noの回答から候補を絞っていき、最終的に想定解をずばり当てることができればクリアとなります。
33+
34+
Yes/Noの情報しか得られないというのがおもしろいところで、今回のお題であるRubyの組み込みメソッドの場合、
35+
36+
> 「引数はいくつ取れますか?」
37+
38+
と数を聞くことはできませんが、
39+
40+
> 「引数の数は決まっていますか?」
41+
> 「引数は2個以下ですか?」
42+
43+
のようにして絞り込むことができます。
44+
45+
今回は質問数に制限なしとしたので、
46+
47+
> 「メソッド名は a で始まりますか?」
48+
> 「メソッド名は b で始まりますか?」
49+
>
50+
51+
と繰り返して頭文字を特定し、
52+
53+
> 「メソッド名の2文字目は a ですか?」
54+
55+
と2文字目以降も順次確定していけば機械的に解けるわけですが、そこはRubyKaigiというテックカンファレンスの場ですので、みなさん手数を減らすべく、
56+
57+
> 「メソッド名は a から m の間で始まりますか?」
58+
> 「いいえ」
59+
> 「メソッド名は n から t の間で始まりますか?」
60+
> 「はい」
61+
>
62+
63+
のように二分探索を意識したり、
64+
65+
> 「文字列を返しますか?」
66+
> 「破壊的メソッドですか?」
67+
68+
とメソッドの挙動から絞り込むなど、工夫して楽しんでいただきました。
69+
70+
## Day 1
71+
72+
初日のお題は、知らない人もいるだろうけど、ぜひ知っていざというときに活用してほしいメソッドを選びました。
73+
74+
### Q&A
75+
76+
| 質問 | 回答 |
77+
| -------------- | ---- |
78+
| 引数を取りますか? | はい |
79+
| 引数を複数取りますか? | はい |
80+
| 無引数でも使えますか? | はい |
81+
| ブロックは取りますか? | いいえ |
82+
| Numericの派生クラスに属しますか? | いいえ |
83+
| 特異メソッドですか? | はい |
84+
| メソッド名は t から z で始まりますか? | はい |
85+
|| |
86+
| R から始まるクラスまたはモジュールに属しますか? | はい |
87+
88+
### 解答編
89+
90+
<details><summary>答えは…</summary>
91+
<p>
92+
93+
**Regexp.union** でした!
94+
95+
</p>
96+
</details>
97+
98+
<details><summary>解説</summary>
99+
<div>
100+
101+
ドキュメントはこちら: [英語版](https://docs.ruby-lang.org/ja/3.3/method/Regexp/s/union.html) / [日本語版](https://docs.ruby-lang.org/en/3.3/Regexp.html#method-c-union)
102+
103+
引数として任意の数のパターン(RegexpまたはStringのリスト or 配列)を受け取り、そのいずれかにマッチするRegexpオブジェクトを返します。
104+
105+
```ruby
106+
words = %w[WED ONE Zero]
107+
Regexp.union(words)
108+
#=> /WED|ONE|Zero/
109+
110+
Regexp.union(/\d+/, /[a-z_]\w+/i)
111+
#=> /(?-mix:\d+)|(?i-mx:[a-z_]\w+)/
112+
# 正規表現オプションも考慮されます。
113+
114+
Regexp.union("Ruby", "Rust")
115+
#=> /Ruby|Rust/
116+
# /Ru(?:by|st)/ のような最適化はしてくれません。
117+
118+
# 何も渡さないと、「何にもマッチしない正規表現」が返ります。
119+
Regexp.union()
120+
#=> /(?!)/
121+
```
122+
123+
Ruby 1.8.1で追加されました。1.8.7から、配列も受け付けるようになりました。
124+
125+
ユースケースは確かに存在しますが、手で実装しようとすると、文字列の場合だけを考えても以下のように意外と煩雑です。ここで `Regexp.escape` を忘れると、 `.``+` があるときに意図せぬ挙動をしてしまいますし、信頼できない入力から生成するときは脆弱性にもつながります。
126+
127+
```ruby
128+
Regexp.new(words.map { |w| Regexp.escape(w) }.join("|"))
129+
```
130+
131+
他の言語には同等機能が見られませんが、Stack Overflowなどでは質問例があり、愚直に実装するよう回答されていました。
132+
133+
そうしたかゆいところに手が届くような機能を標準で用意しているところが、実にRubyらしいと思います。
134+
Rubyの中ではもちろん、他の言語・ライブラリにも広まってほしいと思い、Day 1のお題にしました。
135+
136+
</div>
137+
</details>
138+
139+
## Day 2
140+
141+
二日目は、少しクセ球を投じてみました。
142+
143+
### Q&A
144+
145+
| 質問 | 回答 |
146+
| -------------- | ---- |
147+
| 文字列を引数に取りますか? | はい |
148+
| 数値を引数に取りますか? | はい |
149+
| 無引数でも使えますか? | はい(でもそういう使い方はしないかも) |
150+
| ブロックは取りますか? | いいえ |
151+
| Ruby 2.0に存在しましたか? | いいえ |
152+
| 特異メソッドですか? | いいえ |
153+
| メソッド名は m から s で始まりますか? | はい |
154+
|| |
155+
| K から始まるクラスまたはモジュールに属しますか? | はい |
156+
| 標準出力に何か出力しますか? | はい |
157+
158+
### 解答編
159+
160+
<details><summary>答えは…</summary>
161+
<p>
162+
163+
**Kernel#pp** でした!
164+
165+
</p>
166+
</details>
167+
168+
<details><summary>解説</summary>
169+
<div>
170+
171+
ドキュメントはこちら: [英語版](https://docs.ruby-lang.org/en/3.3/Kernel.html#method-i-pp) / [日本語版](https://docs.ruby-lang.org/ja/3.3/class/Kernel.html#M_PP)
172+
173+
`p` の高機能版です。要素の多い配列やハッシュオブジェクトも行折り返しやインデントを使って読みやすく表示してくれます。
174+
175+
Ruby 2.5から、 `require "pp"` しないでも使えるようになりました。
176+
177+
勘のいい人は、「requireしないと使えないものは含まない」からメタ読みしたかも?
178+
179+
IRBやrails consoleでも `pp` 形式の出力がサポートされています。
180+
インタラクティブなセッションで、大きなデータ構造をきれいに見られるのは `pp` のおかげですね。
181+
182+
たとえばですが、ボードゲームの盤面、木構造のデータなどを現すクラスに `pretty_inspect` を実装してやると、 `pp` するだけできれいに二次元表示できるようになり、開発の助けになることでしょう。
183+
184+
Day 1の `Regexp.union` に続き、Day 2の `pp` (prettyprint)も田中哲(akr)さんの作品です!
185+
186+
</div>
187+
</details>
188+
189+
## Day 3
190+
191+
三日目は、最終日ということでちょっといじわるな問題でした。
192+
193+
### Q&A
194+
195+
| 質問 | 回答 |
196+
| -------------- | ---- |
197+
| 文字列を引数に取りますか? | いいえ |
198+
| 引数を取りますか? | いいえ |
199+
| ブロックは取りますか? | はい |
200+
| メソッド名は a から t で始まりますか? | いいえ |
201+
| Ruby 2.0に存在しましたか? | いいえ |
202+
| Ruby 2.5に存在しましたか? | はい |
203+
|| |
204+
| 仕事で使いますか? | おそらくいいえ |
205+
| 別名がありますか? | はい |
206+
207+
### 解答編
208+
209+
<details><summary>答えは…</summary>
210+
<p>
211+
212+
**Kernel#yield_self** でした!
213+
214+
</p>
215+
</details>
216+
217+
<details><summary>解説</summary>
218+
<div>
219+
220+
ドキュメントはこちら: [英語版](https://docs.ruby-lang.org/en/3.3/Kernel.html#method-i-yield_self) / [日本語版](https://docs.ruby-lang.org/ja/3.3/class/Object.html#I_THEN)
221+
222+
文字通り `yield self` してその値を返すメソッドです。ブロックを渡さない場合の特殊処理を除くと、文字通りこういうメソッドです。
223+
224+
```ruby
225+
module Kernel
226+
def yield_self
227+
yield self
228+
end
229+
end
230+
```
231+
232+
オブジェクトに対する加工をメソッドチェーンで書きたいときに使います。
233+
234+
```ruby
235+
article.title.yield_self { |t| format("「%s」", title) }.to_json
236+
```
237+
238+
機能要望と賛同の声は強かったもののいい名前が見つからず、さんざん大喜利が続きました。
239+
240+
https://bugs.ruby-lang.org/issues/6721
241+
242+
そこで「いい名前」を棚上げし、愚直な名前を採用してRuby 2.5に登場しました。
243+
244+
まもなく、 `then` という名前が提案され、「JavaScriptのPromiseの語彙とぶつかる」という懸念もあったものの、Matzの一声で Ruby 2.6 に別名として追加されました。
245+
246+
https://bugs.ruby-lang.org/issues/14594
247+
248+
今ではみんな新しい名前の `then` しか使っておらず、忘れ去られつつある `yield_self` はRuby一不遇なメソッドと言えるかもしれません。
249+
250+
ところで、このメソッドは実際は `Kernel` モジュールに定義されているのですが、日本語版リファレンスマニュアルでは `Object` クラス内に立項されていました。
251+
そこで、検索しながら答える挑戦者のために、「`Object` クラスに属しますか?」には「はい」と答えるなどの配慮が必要なのがちょっとした誤算でした。😝
252+
253+
</div>
254+
</details>
255+
256+
## 終えての感想
257+
258+
今回は、質問数の上限や時間制限を設けず、緩めのルールで楽しんでいただきました。そして、この形式のゲームに慣れていない方のために、質問例をいろいろと例示してヒントとしてお出ししました。
259+
260+
しかし、それらの例を言われるがままに採用する方はほとんどおらず、渾身の一問を厳選したり、いい質問を自分でひねり出すべく長考に入る姿が多く見られたのがとても印象的でした。そこには、Rubyistのみなさんの「自分で問題を解きたい」「かっこよく解きたい」という魂を感じた次第です。
261+
262+
今回の企画のアキネイターは、問題を用意すること自体は簡単なのですが、解き終えた挑戦者の方に何がしか持ち帰ってもらうべくメソッドの解説やバックストーリーをスライドにしたり、きわどい質問にきちんと対応できるよう想定問答集を準備したり、それでも飛んできた想定外の質問についてスタッフ間で情報共有したりといった部分には手間をかけました。
263+
264+
さて、3日間にわたる長丁場で、特に我々に鮮烈なインパクトを残してくれたのはこんな方々です。
265+
266+
- 1人1問、計たったの3問で正答を導き出してしまったグループ
267+
- 矢継ぎ早の質問で1分もかけずに正解に辿り着いてしまった挑戦者
268+
- 「返り値の型は `self?` ですか?」と鋭角な質問を下さった[RBS](https://github.com/ruby/rbs) & [Steep](https://github.com/soutaro/steep)の作者様
269+
- 初日の早い時間から来ていただき、20分あまりもの格闘の挙げ句、答えのメソッドを覚えていなかったRubyのパパ
270+
- 毎日欠かさずRubyのすべてのコミットに目を通していながらも、PCを開いてIRBからメタプログラミングでメソッドを絞り込むという大技を繰り出してくださった [ruby trunk changes](https://ruby-trunk-changes.hatenablog.com/) の運営者様
271+
272+
これに限らず、すべての対戦にそれぞれドラマがあり、出題者側も大いに楽しませていただきました。
273+
274+
訪れてくださったすべての方々に感謝いたします。みなさん本当にお疲れ様でした!

nuxt.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default defineNuxtConfig({
2929
documentDriven: true,
3030
highlight: {
3131
theme: 'monokai',
32-
preload: ['python', 'lua']
32+
preload: ['kotlin', 'lua', 'python', 'ruby']
3333
}
3434
},
3535
gtag: {
@@ -38,7 +38,7 @@ export default defineNuxtConfig({
3838
dayjs: {
3939
locales: ['ja'],
4040
plugins: ['localizedFormat'],
41-
defaultLocale: 'ja',
41+
defaultLocale: 'ja'
4242
},
4343
nitro: {
4444
prerender: {
84.4 KB
Loading
84.4 KB
Loading

0 commit comments

Comments
 (0)