エンジニアリンググループの星川 (id:oboenikui) です。
RubyWorld Conferenceお疲れ様でした! また、ブースに来ていただいた皆様、ありがとうございました! 弊社はRubyスポンサーとして協賛しており、現地には6人が参加しました。 前日に現地入りし、セッション聴講、ブースでの交流、松江のグルメを堪能と、充実した3日間を過ごせました!
参加レポートはこちらの記事にまとまっています。
さて、本記事では、弊社ブースにて2日間にわたり開催しました難読Rubyクイズの解説をしていきます。 ブースに来られなかった皆様は、ぜひ答えを見る前に考えてみてください!
なお、作問は全て、以下の記事を書いたowlが担当しています。
Day 1-1
次のうち、 !??[??]
の正しい実行結果は?
ヒント: !??[?!]
は true
になります。
- true
- false
- nil
- Error
・
・
・
・
・
・
・
・
正解は……
2番! false
です!
解説
まず、?(任意の1文字)
は文字リテラルと呼ばれ、?の後に文字を1字指定した場合はその文字を表す文字列を返します。つまり、 ??
は "?"
、?!
は "!"
と同義になります。
問題文を書き換えると、こうなります。
# 元のコード !??[?!] !??[??] # 書き換え後 !"?"["!"] !"?"["?"]
次に評価されるのは、 string[string]
の部分です。こちらは、[]
内の文字列が左の文字列のsubstringの場合はそのsubstringを、そうでない場合はnil
を返します。string[regexp]
で正規表現にマッチした部分を返すのによく似ていますね。
この仕様から、 "?"["!"]
は nil
, !"?"["?"]
は "?"
となります。
最後に、1文字目の !
は否定を表しています。Rubyは nil
と false
以外のすべての式を true
と判定するので、 nil
の否定は true
, "?"
の否定は false
となります。
よって、問題の !??[??]
は false
となります。
みなさんの回答
25名中18名の方が正解しました! ヒントを頼りに答えてる方が多くいらっしゃった印象でした。
Day 1-2
次のうち、 !%.!..!
の実行結果はどうなるでしょうか?
- true
- false
- nil
- Error
・
・
・
・
・
・
・
・
正解は……
1番! true
です!
解説
Rubyでは、%記法の囲み文字に一部を除く任意の文字を使うことができます。囲われた部分は通常のクオートやダブルクオートで囲った文字列と同じ扱いになりますので、 %.!.
は "!"
と同義です。
次に、最後の .!
は !
メソッドの呼び出しです。このメソッドは BasicObject
にメソッドとして定義されており、自分自身の真偽値の否定を返します。よって、 %.!..!
の部分は !"!"
と同値になり、 false
となります。
最後に、1文字目の !
で否定しているので、答えは true
になります。
みなさんの回答
27名中11名の方が正解しました! 結構回答がばらけていて、良い問題だったのではないでしょうか。
Day 1-3
次のうち、 ?%%/?%/
の正しい実行結果は?
ヒント: ??%/?%/
は "?"
になります。
- "?"
- "%"
- nil
- Error
・
・
・
・
・
・
・
・
正解は……
4番! Error
です!
解説
まず、1-1で解説したように、 ??
は "?"
、 ?%
は "%"
と同義です。
次に、Stringには %
メソッドがあり、引数(%の右側のオブジェクト)をsprintfフォーマットでフォーマットした結果を返します。(例えば、 "%05d" % 123
は "00123"
となります。)
今回の3文字目の %
は、このメソッドになります。
最後に、 %
メソッドに渡すオブジェクトが /?%/
です。これは正規表現リテラルです。つまり、??%/?%/
は "?" % /?%/
のように構文解析され、 /?%/
を "?"
でフォーマットする処理、 ?%%/?%/
は /?%/
を "%"
でフォーマットする処理と解釈されます。ここで、 "?"
は引数に何を渡されても、sprintfフォーマットの %d
のような指定がないので "?"
を返します。一方、 "%"
というのはタイプ指定がないため、不正なsprintfフォーマットです。よって、エラーとなります。
みなさんの回答
25名中6名の方が正解しました!単純にヒントを見ると "%"
となりそうなことに引っかかってしまった方が多くいらっしゃいました。
Day 2-1
次のうち、 +-+-+-1===-+-+-+1
の正しい実行結果は?
- true
- false
- 0
- Error
・
・
・
・
・
・
・
・
正解は……
1番! true
です!
解説
Integerには +@
メソッドと -@
メソッドがあり、前者はそのままの値を、後者はselfの符号を反転させたものを返します。
+-+-+-1
は -1 に対して2回、 -+-+-+1
は 1 に対して3回 -@
メソッドが呼ばれているので、両辺とも -1
となります。それらの比較 ===
なので、答えは true
です。
ちなみにIntegerの ===
は ==
のエイリアスです。
みなさんの回答
17名中13名の方が正解しました! 素直に解釈すれば正解できる問題だったのですが、逆に裏をかいてしまった方々が間違えてしまったようです。
Day 2-2
次のうち、 ().|(0).!()
の正しい実行結果は?
- true
- false
- 0
- Error
・
・
・
・
・
・
・
・
正解は……
2番! false
です!
解説
最初の ()
は空の式で、 nil
になります。
.|(0)
.!()
の部分はそれぞれメソッド呼び出しです。
前者は NilClass
に定義されたメソッドで、引数が真なら true
を、偽なら false
を返します。
後者は1-2にも出てきた、真偽値の否定を返すメソッドです。
すなわち
!(nil | 0)
と等価です。 nil | 0
は true
になるので、答えは false
です。
みなさんの回答
16名中4名の方が正解しました! Errorと答えられた方は、 |
のあたりでエラーとなると予想されていた方が多くいらっしゃった印象でした。
Day 2-3
次のうち、 (?a..?A).to_a[-2]
の正しい実行結果は?
- "z"
- "`"
- nil
- 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クイズのようなコードに遭遇することはありませんので、今回のクイズに全問正解できた方も、残念ながら間違えてしまった方も、ご応募お待ちしております。