Slide 1

Slide 1 text

Ruby "enbugging" quiz の別解を求めて 2024.06.04 kusaka (@j5c8k6m8)

Slide 2

Slide 2 text

©2024 Coincheck Inc. 自己紹介

Slide 3

Slide 3 text

©2024 Coincheck Inc. ● アプリケーション基盤G に所属 ○ Rails update ○ モジュラモノリス推進 ○ 認可 / 認証 ○ 開発規約の整備 自己紹介 ● 元 SIer 出身 ● たまに Qiita とか Zenn とか ● kusaka と読み替えて下さい

Slide 4

Slide 4 text

©2024 Coincheck Inc. Ruby "enbugging" quiz とは

Slide 5

Slide 5 text

©2024 Coincheck Inc. Ruby "enbugging" quiz https://ruby-quiz-2024.storesinc.tech/ RubyKaigi2024 の STORES さんの企画 Rubyコミッタである、遠藤(mame) さんが作成 mame さんの quiz は毎年の RubyKaigi の楽しみの一つ (画像はmame さんのセッションより引用) https://speakerdeck.com/mame/good-first-issues-of-typeprof?slide=3

Slide 6

Slide 6 text

©2024 Coincheck Inc. Ruby "enbugging" quiz ● 全部で 12 Stage (3Stage/日. 全問解いた後の extra で追加 3stage) ● クリア条件: Expected error に記載のエラーを発生させること ● 手順: ○ 最初から表示されている コードの一部を書き換える ■ 書き換えた量 (diff) に応じて Score が変わる ○ 実行ボタン   を押す ■ Expected error のエラーが発生したらクリア n → nil クリア! (例年より簡単?..)

Slide 7

Slide 7 text

©2024 Coincheck Inc. Ruby "enbugging" quiz Day 1 できた! 3問で18 なるほど。。 自力で解けると気持ちいいので、まずはチャレンジを ただ解くだけではなく、低スコアチャレンジもできるという2段構えの quiz (すべて、Score 1での解が用意されている?) 間違い探しのように、 答えを知っちゃうと、 探す楽しみがなくなるタイプ

Slide 8

Slide 8 text

©2024 Coincheck Inc. Ruby "enbugging" quiz 5/27 に STORES さんの テックブログから mame さんの公式解説記事 も出していただいてます! (予想通り、全ての問題が Score 1で解けます) https://product.st.inc/entry/2024/05/27/113038 (※ ネタバレ防止のため、マスク) 私はここでいう 別の回答 で解いたな、、 他にも解説記事で紹介されてない、 Score 1 の別解があるのでは? Solver プログラム を作って、別解を確認してみよう! 必読!

Slide 9

Slide 9 text

©2024 Coincheck Inc. Solver を作る

Slide 10

Slide 10 text

©2024 Coincheck Inc. Solver を作る https://github.com/cc-kusaka/rubykaigi2024_after_enbugging_quiz_solver 作成した Solver(リポジトリ) の URL と QRコード この後の話の ネタバレ含む Ruby "enbugging" quiz のエラー形式は、 markdown friendly に改善された 3.4.0 形式なことに注意 (引用) mame さんのセッション ("Good first issues of TypeProf") より https://speakerdeck.com/mame/good-first-issues-of-typeprof?slide=2 コードは ruby 3.4.0 の環境で 実行してください

Slide 11

Slide 11 text

©2024 Coincheck Inc. Solver を作る まずは、問題を json 形式で保存 (stage 毎の expected_error と source を保持した hash)

Slide 12

Slide 12 text

©2024 Coincheck Inc. Solver を作る 用意したJSON読み込み 結果JSON出力 各文字位置 に対して、 文字削除 or ASCII の各文字挿入 のケース で 解答となる source を作成 eval で実行 SyntaxError は StandardError ではない点に注意 正解かの判定

Slide 13

Slide 13 text

©2024 Coincheck Inc. Solver を作る 警告を抑止 (RUBYOPT=”W0”) し、 評価コード中の puts の標準出力を無視 ( 1> /dev/null) して、 実行! 想定外のエラーが発生!

Slide 14

Slide 14 text

©2024 Coincheck Inc. eval で実行 eval での評価コードが外側の変数を、 参照して書き換えてしまっていた Solver を作る

Slide 15

Slide 15 text

©2024 Coincheck Inc. Solver を作る (instance_eval で)

Slide 16

Slide 16 text

©2024 Coincheck Inc. Solver を作る(instance_eval) TOPレベルの変数を削除し、 instance_eval を利用した、 実行環境用のクラスを用意 毎回 instance を生成

Slide 17

Slide 17 text

©2024 Coincheck Inc. Solver を作る(instance_eval) 正常終了!したが.. 結果ファイルにStage 7 の回答が出力されていない

Slide 18

Slide 18 text

©2024 Coincheck Inc. Solver を作る(instance_eval) 発生したエラーに特異クラスの module名が入ってしまい、 Expected error が一致しなくなってしまっていた。 Actual error: private method 'main' called for an instance of #::C (NoMethodError) Stage 7 のケース(Class名の問題) 以外にも、 グローバル変数やクラス変数がリセットされない問題もありそう。 問題中にclass 定義

Slide 19

Slide 19 text

©2024 Coincheck Inc. Solver を作る (コマンド実行 で) Take3

Slide 20

Slide 20 text

©2024 Coincheck Inc. ● バッククォートでコマンド実行して結果を受け取る ● 標準エラー出力を、標準出力に流して受け取る ● エラー出力には、行数なども含まれるためinclude? で判定する Solver を作る(コマンド実行) コードをファイル出力

Slide 21

Slide 21 text

©2024 Coincheck Inc. ※ esa_io さんのブログ (https://docs.esa.io/posts/509) で、 solver (https://github.com/fukayatsu/ruby-quiz-2024/blob/main/solve.rb) が紹介されていました。 こちらは、Thread を利用した多重処理で重さを回避しているようでした。 (エラー取得も Open3 モジュールを利用) 実行が終わらない (全体だと数時間かかる) Solver を作る(コマンド実行) Stage 1 に限定して 15分21秒 instance_eval 版は 0.3秒

Slide 22

Slide 22 text

©2024 Coincheck Inc. Solver を作る (forkを使って) Take4

Slide 23

Slide 23 text

©2024 Coincheck Inc. Solver を作る(fork) Stage7 を考慮し、Object の class_eval で実行。 別プロセスで実行するので、 グローバル変数を含む各種変数が、 前の施行の影響を受けない。 fork でプロセスを複製し、結果を IO.pipe でやりとり (pipe 周りでコードがちょっと長くなる) 都度 wait をするので実質 1並行

Slide 24

Slide 24 text

©2024 Coincheck Inc. Solver を作る(fork) 全体でも 3分22秒 で終了 並列処理版にす る 並列処理にする 全体で 1分5秒 に短縮

Slide 25

Slide 25 text

©2024 Coincheck Inc. Solver を作る(fork) 実際にやってみても、 undefined local variable or method になり、クリアにならない ‘j’ を入れる誤解答が提示 ‘j’ を入れる誤解答が提示

Slide 26

Slide 26 text

©2024 Coincheck Inc. Solver を作る(fork) require ‘json’ で ‘j’ メソッドが生えていた! https://github.com/ruby/ruby/blob/d50404d6fe9dcd991dbad4f8757d23d38d1b5b80/ext/json/lib/json/common.rb#L658-L663

Slide 27

Slide 27 text

©2024 Coincheck Inc. Solver を作る (まとめ)

Slide 28

Slide 28 text

©2024 Coincheck Inc. Solver を作る(まとめ) ● 隔離された Clean な実行環境を用意するのには、一定コストが必要 ● 現状では fork でプロセスコピーが、(性能を含めた)一定解か。(require 問題はあるが) # Solver の内容 実行時間 (Stage 1のみ) 出力結果 1 (グローバルスコープで) eval を利用 - ● クロージャを参照して失敗 2 (スコープを意識して) instance_eval を利用 0.3秒 ● 実行環境のモジュール名の差異が発生 ● クラス変数やグローバル変数を実行都度リセットできない 3 (ファイル出力と) コマンド実行を利用 (single) 15分 21秒 ● 基本的には問題なし ● Error の一致判定は標準エラー出力の部分一致を利用 4 fork を利用 (single) 19.3秒 ● 評価前の `require` などで 環境に差異が発生 5 fork を利用 (multi) 6.6秒 ● 同上

Slide 29

Slide 29 text

©2024 Coincheck Inc. # Solver の内容 実行時間 (Stage 1のみ) 出力結果 1 (グローバルスコープで) eval を利用 - ● クロージャを参照して失敗 2 (スコープを意識して) instance_eval を利用 0.3秒 ● 実行環境のモジュール名の差異が発生 ● クラス変数やグローバル変数を実行都度リセットできない 3 (ファイル出力と) コマンド実行を利用 (single) 15分 21秒 ● 基本的には問題なし ● Error の一致判定は標準エラー出力の部分一致を利用 4 fork を利用 (single) 19.3秒 ● 評価前の `require` などで 環境に差異が発生 5 fork を利用 (multi) 6.6秒 ● 同上 Solver を作る(まとめ) ● 隔離された Clean な実行環境を用意するのには、一定コストが必要 ● 現状では fork でプロセスコピーが、(性能を含めた)一定解か。(require 問題はあるが) ○ Ruby に Namespace が導入されるのに期待 ■ 少なくとも、require ‘json’ 問題は解決可能なはず ■ instance_eval のような方法でも実現可能になるかも

Slide 30

Slide 30 text

©2024 Coincheck Inc. 別解を見る 別解あったよ

Slide 31

Slide 31 text

©2024 Coincheck Inc. 別解を見る # Expected error 解数(※1) 別解 Stage 1 undefined method '+' for nil (NoMethodError) 11 有 Stage 2 undefined method 'downcase' for nil (NoMethodError) 16 無 Stage 3 wrong number of arguments (given 2, expected 3) (ArgumentError) 1 無 Stage 4 nil can't be coerced into Integer (TypeError) 9 有 Stage 5 index -4 too small for array; minimum: -3 (IndexError) 1 無 Stage 6 negative argument (ArgumentError) 7 有 Stage 7 private method 'main' called for an instance of C (NoMethodError) 5 無 Stage 8 cyclic include detected (ArgumentError) 7 有 Stage 9 wrong number of arguments (given 1, expected 0) (ArgumentError) 11 有 Stage A cannot clamp with an exclusive range (ArgumentError) 10 無 Stage B 0: 1 === 0 does not return true (NoMatchingPatternError) 1 無 Stage C invalid radix 52 (ArgumentError) 2 無 (※1) 空白文字の違いは1でカウント 面白いと思った 3つの別解を紹介します(ネタバレ含む)

Slide 32

Slide 32 text

©2024 Coincheck Inc. 別解を見る (Stage1)

Slide 33

Slide 33 text

©2024 Coincheck Inc. 別解を見る (Stage 1) 解は11個 9パターン 解説でも4パターンが紹介されている

Slide 34

Slide 34 text

©2024 Coincheck Inc. += を使うことでnil 初期化 .追加で、メソッドチェーンにして 右辺から評価させる (1_000.p は no method だけど) 1文字メソッドの p を追加 別解を見る (Stage 1) 削除, ‘#’, ‘@’, ‘$’ が公式解説でも言及されていた ‘+’, ‘.’, ‘p’ を追加する方法(上記以外の箇所も含めて) が別解

Slide 35

Slide 35 text

©2024 Coincheck Inc. 別解を見る (Stage6)

Slide 36

Slide 36 text

©2024 Coincheck Inc. 解は7個 6パターン 「どこかに、改行を入れる」 という解がある 別解を見る (Stage 6)

Slide 37

Slide 37 text

©2024 Coincheck Inc. 配列として代入できる記法 (知らなかった) 解説では、n = *10 が紹介。 別解を見る (Stage 6) 公式の解説もとにかく面白いので すが(コメントがひっかけ) そのコメントを利用する別解

Slide 38

Slide 38 text

©2024 Coincheck Inc. 別解を見る (Stage4)

Slide 39

Slide 39 text

©2024 Coincheck Inc. 別解を見る (Stage 4) 解は9個 7パターン 「どこかに、’%’ を入れる」 という解がある

Slide 40

Slide 40 text

©2024 Coincheck Inc. 別解を見る (Stage 4)

Slide 41

Slide 41 text

©2024 Coincheck Inc. 別解を見る (Stage 4) 文字列リテラルの % 記法の区切 り文字がスペース と理解して読むと以下になる

Slide 42

Slide 42 text

©2024 Coincheck Inc. Score1 での解の数 紹介していない別解もあるので、興味ある人は 以下の回答(と公式の解説)を見てみてください。 https://github.com/cc-kusaka/rubykaigi2024_after_enbugging_quiz_solver/blob/main/answers3.json # Expected error 解数(※1) 別解 Stage 1 undefined method '+' for nil (NoMethodError) 11 有 Stage 2 undefined method 'downcase' for nil (NoMethodError) 16 無 Stage 3 wrong number of arguments (given 2, expected 3) (ArgumentError) 1 無 Stage 4 nil can't be coerced into Integer (TypeError) 9 有 Stage 5 index -4 too small for array; minimum: -3 (IndexError) 1 無 Stage 6 negative argument (ArgumentError) 7 有 Stage 7 private method 'main' called for an instance of C (NoMethodError) 5 無 Stage 8 cyclic include detected (ArgumentError) 7 有 Stage 9 wrong number of arguments (given 1, expected 0) (ArgumentError) 11 有 Stage A cannot clamp with an exclusive range (ArgumentError) 10 無 Stage B 0: 1 === 0 does not return true (NoMatchingPatternError) 1 無 Stage C invalid radix 52 (ArgumentError) 2 無 (※1) 空白文字の違いは1でカウント

Slide 43

Slide 43 text

©2024 Coincheck Inc. さいごに

Slide 44

Slide 44 text

©2024 Coincheck Inc. ● まずは、何よりも、本 LT の前提となる 「Ruby "enbugging" quiz」を 作成/提供していただいた、Stores さん、 mame さん、関係者の方々に感謝します。 ● mame さんが提供している quiz は、ものすごく楽しいので、ぜひ参加してください。 ● 変わったプログラムを書いたり読んだりすると、言語の背景が薄く見えるような感覚があっ て楽しいですね。 ● 来年は TRICK2025 の開催も決定しているので、チャレンジしたいです(宣言) さいごに

Slide 45

Slide 45 text

©2024 Coincheck Inc. 業務に関係ないけどこういうことをやる利点的なやつ書く? rubykaigi2024 と mame さんへの感謝 coincheck の採用とかについて? さいごに https://hrmos.co/pages/coincheck/jobs/0000551

Slide 46

Slide 46 text

©2024 Coincheck Inc. さいごに ご清聴ありがとうございました!