エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

難読RubyクイズReturns Day1 & Day2 @RubyWorldConference2022

エンジニアリンググループの星川 (id:oboenikui) です。

RubyWorld Conferenceお疲れ様でした! また、ブースに来ていただいた皆様、ありがとうございました! 弊社はRubyスポンサーとして協賛しており、現地には6人が参加しました。 前日に現地入りし、セッション聴講、ブースでの交流、松江のグルメを堪能と、充実した3日間を過ごせました!

参加レポートはこちらの記事にまとまっています。

www.m3tech.blog

さて、本記事では、弊社ブースにて2日間にわたり開催しました難読Rubyクイズの解説をしていきます。 ブースに来られなかった皆様は、ぜひ答えを見る前に考えてみてください!

なお、作問は全て、以下の記事を書いたowlが担当しています。

www.m3tech.blog

Day 1-1

次のうち、 !??[??] の正しい実行結果は?
ヒント: !??[?!]true になります。

  1. true
  2. false
  3. nil
  4. Error








正解は……

2番! false です!

解説

まず、?(任意の1文字)文字リテラルと呼ばれ、?の後に文字を1字指定した場合はその文字を表す文字列を返します。つまり、 ??"?"?!"!" と同義になります。

問題文を書き換えると、こうなります。

# 元のコード
!??[?!]
!??[??]
# 書き換え後
!"?"["!"]
!"?"["?"]

次に評価されるのは、 string[string] の部分です。こちらは、[]内の文字列が左の文字列のsubstringの場合はそのsubstringを、そうでない場合はnilを返しますstring[regexp] で正規表現にマッチした部分を返すのによく似ていますね。

この仕様から、 "?"["!"]nil , !"?"["?"]"?" となります。

最後に、1文字目の ! は否定を表しています。Rubyは nilfalse 以外のすべての式を true と判定するので、 nil の否定は true, "?" の否定は false となります。

よって、問題の !??[??]false となります。

みなさんの回答

25名中18名の方が正解しました! ヒントを頼りに答えてる方が多くいらっしゃった印象でした。

Day 1-2

次のうち、 !%.!..! の実行結果はどうなるでしょうか?

  1. true
  2. false
  3. nil
  4. Error








正解は……

1番! true です!

解説

Rubyでは、%記法の囲み文字に一部を除く任意の文字を使うことができます。囲われた部分は通常のクオートやダブルクオートで囲った文字列と同じ扱いになりますので、 %.!."!" と同義です。

次に、最後の .!! メソッドの呼び出しです。このメソッドは BasicObject にメソッドとして定義されており、自分自身の真偽値の否定を返します。よって、 %.!..! の部分は !"!" と同値になり、 false となります。

最後に、1文字目の ! で否定しているので、答えは true になります。

みなさんの回答

27名中11名の方が正解しました! 結構回答がばらけていて、良い問題だったのではないでしょうか。

Day 1-3

次のうち、 ?%%/?%/ の正しい実行結果は?
ヒント: ??%/?%/"?" になります。

  1. "?"
  2. "%"
  3. nil
  4. Error








正解は……

4番! Error です!

解説

まず、1-1で解説したように、 ??"?"?%"%" と同義です。

次に、Stringには % メソッドがあり、引数(%の右側のオブジェクト)をsprintfフォーマットでフォーマットした結果を返します。(例えば、 "%05d" % 123"00123" となります。)
今回の3文字目の % は、このメソッドになります。

最後に、 % メソッドに渡すオブジェクトが /?%/ です。これは正規表現リテラルです。つまり、??%/?%/"?" % /?%/ のように構文解析され、 /?%/"?" でフォーマットする処理、 ?%%/?%//?%/"%" でフォーマットする処理と解釈されます。ここで、 "?" は引数に何を渡されても、sprintfフォーマットの %d のような指定がないので "?" を返します。一方、 "%" というのはタイプ指定がないため、不正なsprintfフォーマットです。よって、エラーとなります。

みなさんの回答

25名中6名の方が正解しました!単純にヒントを見ると "%" となりそうなことに引っかかってしまった方が多くいらっしゃいました。

Day 2-1

次のうち、 +-+-+-1===-+-+-+1 の正しい実行結果は?

  1. true
  2. false
  3. 0
  4. Error








正解は……

1番! true です!

解説

Integerには +@ メソッド-@ メソッドがあり、前者はそのままの値を、後者はselfの符号を反転させたものを返します。

+-+-+-1 は -1 に対して2回、 -+-+-+1 は 1 に対して3回 -@ メソッドが呼ばれているので、両辺とも -1 となります。それらの比較 === なので、答えは true です。

ちなみにIntegerの ===== のエイリアスです

みなさんの回答

17名中13名の方が正解しました! 素直に解釈すれば正解できる問題だったのですが、逆に裏をかいてしまった方々が間違えてしまったようです。

Day 2-2

次のうち、 ().|(0).!() の正しい実行結果は?

  1. true
  2. false
  3. 0
  4. Error








正解は……

2番! false です!

解説

最初の ()空の式で、 nil になります。

.|(0) .!() の部分はそれぞれメソッド呼び出しです。

前者は NilClass に定義されたメソッドで、引数が真なら true を、偽なら false を返します。

後者は1-2にも出てきた、真偽値の否定を返すメソッドです。

すなわち

!(nil | 0)

と等価です。 nil | 0true になるので、答えは false です。

みなさんの回答

16名中4名の方が正解しました! Errorと答えられた方は、 | のあたりでエラーとなると予想されていた方が多くいらっしゃった印象でした。

Day 2-3

次のうち、 (?a..?A).to_a[-2] の正しい実行結果は?

  1. "z"
  2. "`"
  3. nil
  4. Error








正解は……

3番! nil です!

解説

これまでの解説から、

("a".."A").to_a[-2]

と等価なのは説明するまでもないですね。

.. は範囲式と呼ばれるもので、Rangeオブジェクトを生成します。整数のRangeを生成するのによく用いられますが、実は文字列でも生成可能です。
また、to_aメソッドはこの範囲を配列に変換します
つまり、例えば ("0".."9").to_a であれば、 ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] を返します。

それでは、 ("a".."A").to_a["a", "b", ..., "z", "A"] かと思いきや、"A" のコードポイントは 0x41"a" のコードポイントは 0x61 で、"a" の方が大きいのです。そのため "a".."A" という範囲に含まれる文字はありません。

つまり、 ("a".."A").to_a は空配列となります。

最後の [-2] は配列アクセスで、後ろから2番目の要素を取りますが、配列が空な場合は nil を返します。

したがって、 nil が正解です。

ちなみに、 (?A..?a).to_a[-2] の場合は、コードポイントが "a" の1つ前の文字になるので、 "`" になります。( "z" ではありません)

みなさんの回答

21名中2名の方が正解しました!

正解者に加え、残念ながら不正解となってしまったものの、Errorと答えた2名の方はコードポイントの引っ掛けに気づいていらっしゃいました。みなさんすごい……

まとめ

挑戦いただいたRubyistの皆様、ありがとうございました!

最近プログラミングの勉強を始めた方から、まつもとゆきひろさんはじめRubyコミッターの方々まで、多くの方にご参加いただきました!

次回がありましたら、またぜひご挑戦ください!

We are hiring!!

エムスリーではRuby/Railsエンジニアを絶賛募集中です。

業務で難読Rubyクイズのようなコードに遭遇することはありませんので、今回のクイズに全問正解できた方も、残念ながら間違えてしまった方も、ご応募お待ちしております。

jobs.m3.com