こんにちは、あるいはこんばんは。 @gessy0129です。
沖縄、行ってきました!
観光、しませんでした!
とても楽しかったです!
今回、ファインディブースでは、Ruby歴×Ruby関連のカンファレンス参加回数は?というアンケートと日替わりでRuby Code Quizを実施しました! 全体的なブースの話はDevRelのまっきーがまとめているのでそちらをご覧ください!
本記事では、エンジニアなら気になるであろうFindy Ruby Code Quizの解説をしていきたいと思います!では、早速見ていきましょう!
一日目 - Rubyバージョンの組み合わせ
問題
下記、実行結果のRubyバージョンの組み合わせを答えてください。 なお、ひとつのバージョンはひとつの選択肢にのみ回答できます。
Ruby versions: 3.3.0 or 3.2.0 or 3.1.0 or 3.0.0
①
irb> Time.new("2024-05-15T11:11:11Z") => 2024-05-15 11:11:11 UTC
②
irb> Time.new("2024-05-15T11:11:11Z") => 2024-01-01 00:00:00 +0000
③
irb> Time.new("2024-05-15T11:11:11Z") <internal:timev>:310:in `initialize': invalid value for Integer(): "2024-05-15T11:11:11Z" (ArgumentError)
④
irb> Time.new("2024-05-15") <internal:timev>:409:in `initialize': no time information (ArgumentError)
選択肢
- ①: 3.2.0、②: 3.0.0、③: 3.1.0、④: 3.3.0
- ①: 3.3.0、②: 3.2.0、③: 3.1.0、④: 3.0.0
- ①: 3.1.0、②: 3.0.0、③: 3.2.0、④: 3.3.0
- ①: 3.0.0、②: 3.1.0、③: 3.3.0、④: 3.2.0
正解と回答率
正解
- ✅ 1. ①: 3.2.0、②: 3.0.0、③: 3.1.0、④: 3.3.0
回答率
- 14.15%
- 53.77%
- 19.81%
- 12.26%
解説
RubyのバージョンアップのNEWSを追いかけていれば解けるという問題です。
Ruby3.3.0のNEWS
https://github.com/ruby/ruby/blob/v3_3_0/NEWS.md
Time.new('2023-12-20') # no time information (ArgumentError)
これを知っていたら④が3.3.0だとわかります。
Ruby3.2.0のNEWS
https://github.com/ruby/ruby/blob/v3_2_0/NEWS.md
Time.new now can parse a string like generated by Time#inspect and return a Time instance based on the given argument.
Time.newはinspectが出力するstringからパースできるようになった。これで、①は3.2.0以上とわかります。
Ruby3.1.0のNEWS
https://github.com/ruby/ruby/blob/v3_1_0/NEWS.md
At the same time, time component strings are converted to integers more strictly now. Time.new(2021, 12, 25, "+07:30") #=> invalid value for Integer(): "+07:30" (ArgumentError) Ruby 3.0 or earlier returned probably unexpected result 2021-12-25 07:00:00, not 2021-12-25 07:30:00 nor 2021-12-25 00:00:00 +07:30.
そもそも3.1.0までは文字列だけをいれるユースケースはなかったようです。3.0.0以前は文字列だけだとよくわからない挙動になっていたけど、この対応でチェックを厳密にしたことで文字列だけだとエラーになったのではないかと推測します。変更意図などはCommiterの方々に解説求めたい部分です。
ただ、このNEWSの内容を全てを記憶してるのは難しすぎになってしまうので、回答の選択肢を4択にしています。4択なので3.3.0の挙動を知っていたら1番目と3番目の2択に絞れると思います。
これに加えて、3.2.0の挙動を知っていたら①は3.2.0以上とわかるので1番目が答えだとわかります。また、3.2.0の挙動を知らなかったとしても、①〜③は全く同じコマンドを実行しており①が正しく挙動しているので①の方が新しいバージョンだと推測できます。これでも1番目が答えだとわかるようになってます。
二日目 - 代入されているオブジェクトはどれ?
問題
fooに代入されているオブジェクトは次のうちどれでしょうか? (Ruby: 3.3.0で実行した場合)
irb(main):001> foo = ??? irb(main):002> foo.include?(3/2) => true irb(main):003> (foo + [3]).inject(:+) => 9 irb(main):004> foo.last => NoMethodError
選択肢
1..3
[1, 2, 3]
Set.new([1, 2, 3])
Enumerator.new { |y| [1, 2, 3].each { |i| y << i } }
正解と回答率
正解
- ✅ 4.
Enumerator.new { |y| [1, 2, 3].each { |i| y << i } }
回答率
- 25.37%
- 4.88%
- 39.02%
- 30.73%
解説
メソッドに対しての出力結果から元の値を当てるという問題形式を使ってみたかった問題です。 そこから逆算して、ちょっと似ているクラス(Range, Array, Set, Enumerator)を当てるようにしてみました。
irb(main):002 の出力は引っ掛けで、3/2 => 1になるのですべてtrueになる。
が、直感的には3/2 => 1.5という考えを利用して、1..3では?と思わせて問題を難しくしました。
irb(main):003 は、同じ値は重複しないというSetの特徴を利用から、Setを除外できるようになっている。また、Rangeには+メソッドがないのでエラーになる。
irb(main):004 は、Setには順番がなく、Enumeratorは外部イテレータなので最後の要素が特定できないという特徴を利用している。
実行結果を見るのが一番早いですね。
選択肢1
irb(main):001> foo = 1..3 => 1..3 irb(main):002> foo.include?(3/2) => true irb(main):003> (foo + [3]).inject(:+) (irb):3:in `<main>': undefined method `+' for an instance of Range (NoMethodError) irb(main):004> foo.last => 3
Rangeには+メソッドがないのでNoMethodErrorになるのと、lastメソッドがあるので結果が返ってきます。
選択肢2
irb(main):001> foo = [1, 2, 3] => [1, 2, 3] irb(main):002> foo.include?(3/2) => true irb(main):003> (foo + [3]).inject(:+) => 9 irb(main):004> foo.last => 3
lastメソッドがあるので結果が返ってきます。
選択肢3
irb(main):001> foo = Set.new([1, 2, 3]) => #<Set: {1, 2, 3}> irb(main):002> foo.include?(3/2) => true irb(main):003> (foo + [3]).inject(:+) => 6 irb(main):004> foo.last (irb):4:in `<main>': undefined method `last' for an instance of Set (NoMethodError)
同じ値は重複しないというSetの特徴から003の結果が6になります。
選択肢4
irb(main):001> foo = Enumerator.new { |y| [1, 2, 3].each { |i| y << i } } => #<Enumerator: ...> irb(main):002> foo.include?(3/2) => true irb(main):003> (foo + [3]).inject(:+) => 9 irb(main):004> foo.last (irb):4:in `<main>': undefined method `last' for an instance of Enumerator (NoMethodError)
正解ですね!!
有識者の方に解説モトム
Setには順番がないと書きましたが、firstはあります。なので、こうなります。
irb(main):001> foo = Set.new([1, 2, 3]) => #<Set: {1, 2, 3}> irb(main):002> foo.first => 1
なんでfirstがあるのか、詳しい方のコメントを待っています。Enumerableを継承しているからと言われたらそれまでと言えばそれまでですがw
三日目 - 正しい出力結果は?
問題
次のコードを実行した出力結果で正しいのはどれでしょう? (文字コード UTF-8、Ruby: 3.3.0で実行した場合)
hoge = [?1,?2].zip([?3,?4], [?5,?6]).transpose.flatten hoge.reject! hoge = hoge.map{|n| n.ord}.sort puts hoge.join(", ")
選択肢
1, 2, 3, 4, 5, 6
6, 5, 4, 3, 2, 1
49, 50, 51, 52, 53, 54
54, 53, 52, 51, 50, 49
正解と回答率
正解
- ✅ 3.
49, 50, 51, 52, 53, 54
回答率
- 17.19%
- 14.06%
- 60.94%
- 7.81%
解説
問題文のコードを読みにくくするため、わざとインデントやスペースを入れていない。というちょっとイジワルをしました。
[Integer, Integer]に対して、なにかしら変換するのはひっかけるポイントが少ないので、String→Integerへ変換するようにして、複雑さを増すようにしています。
# zipメソッドを使う人はあまりいなそうなので、採用 > hoge = [?1,?2].zip([?3,?4],[?5,?6]) => [["1", "3", "5"], ["2", "4", "6"]] # ノーマルなRubyistは2次元配列を扱うことが少ないはずなので、transposeメソッドを採用 irb(main):003> hoge = hoge.transpose => [["1", "2"], ["3", "4"], ["5", "6"]] # Rubyistはflattenを結構使っている気がする。ここは配列を入れ子にしたくないから、採用 irb(main):004> hoge = hoge.flatten => ["1", "2", "3", "4", "5", "6"] # なにかやっていそうだけど、Enumeratorに変えているだけ irb(main):006> hoge = hoge.reject! => #<Enumerator: ...> # ordメソッドが、StringからIntgerへ変換することを知っているかチェック # sortメソッドが降順 or 昇順どちらで並び替えているか試す irb(main):007> hoge = hoge.map{|n|n.ord}.sort => [49, 50, 51, 52, 53, 54] irb(main):008> puts hoge.join(", ") 49, 50, 51, 52, 53, 54
さいごに
4/1 からクイズ作成を開始して、約4日ほどでほぼほぼ今の形まで出来上がりました。ラフで色んな案を出して、煮詰めていく過程がとても楽しかった(らしい)です。
また、 knuさんがブースに遊びに来てくれてPostしてくれていたのがとても嬉しかったです!来ていただいてありがとうございました!
Findyさんの問題良問ですね!
— Akinori MUSHA (@knu) 2024年5月16日
自分がSetとEnumeratorとirbセッション4行目の登場人物すべての作者なのに、足し算を勘違いして誤答してしまったw#RubyKaigi pic.twitter.com/qkXyemZZkn
問題は@hamchance0215、@aiandrox、@sontixyouが作成してくれました!ありがとうございました!
5/28には「After RubyKaigi 2024〜メドピア、ZOZO、Findy〜」として、メドピア株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2024の振り返りを行います。
LTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!!
ファインディでは、これからもRubyを積極的に活用して、Rubyとともに成長していければと考えております。
そして、一緒に働くメンバーを絶賛募集中です。
興味がある方はこちらから ↓