Upgrade to Pro — share decks privately, control downloads, hide ads and more …

至極の難問言語仕様クイズ 〜最強のマルチリンガル決定戦〜

至極の難問言語仕様クイズ 〜最強のマルチリンガル決定戦〜

PIXIVDEVMEETUPの前夜祭的な企画で開催したエンジニアクイズ大会のスライドです

orekyuu

May 21, 2021
Tweet

More Decks by orekyuu

Other Decks in Programming

Transcript

  1. 至極の難問言語仕様クイズ
    〜最強のマルチリンガル決定戦〜
    2021.5.6

    View Slide

  2. 至極の難問言語仕様クイズとは
    ● YAML / PHP / Ruby / JavaScript / Java の5言語から4択クイズを出題
    ○ バージョンが明記されてなければ最新の実装で動くものと思ってください
    ● 最も正答率の高かった人が複数のプログラミング言語を極めし
    王者!
    ● 最強のマルチリンガルには、王者にふさわしい豪華景品を用意しています

    View Slide

  3. 景品1
    最強のマルチリンガルRoleを付与
    目立つこと間違いなし!!!

    View Slide

  4. 景品2
    重箱の隅をつつく人をモチーフ
    にした「多言語王トロフィー」
    ※複数名優勝の場合は抽選 1名

    View Slide

  5. ルール説明

    View Slide

  6. フォームでご回答ください

    View Slide

  7. 󰢄 禁止事項
    ● irbやdevconsoleなどで実行して動作確認しないでください
    ● ぐぐらないでください
    ● 答えが出てからフォームに回答しないでください
    ● #エンジニアクイズ大会 に正解に近づくヒント、もしくは解答を
    書かないでください
    ● 正々堂々最強のエンジニアになりましょう

    View Slide

  8. 󰢐 推奨事項
    ● #エンジニアクイズ大会 での実況・感想など
    ● Twitterへの感想の投稿など
    ○ #pixivdevmeetup をつけていただけると嬉しいです

    View Slide

  9. 練習問題
    必ず解答ください

    View Slide

  10. 1. Hello Worldが出力される
    2. Hello pixivが出力される
    3. Hell pixivが出力される
    4. 何も出力されない
    Ruby
    puts 'Hello pixiv'

    View Slide

  11. 1. Hello Worldが出力される
    2. Hello pixivが出力される
    3. Hell pixivが出力される
    4. 何も出力されない
    Ruby
    puts 'Hello pixiv'

    View Slide

  12. 12
    なぜ?
    ● putsは標準出力をするためのメソッド
    ● 渡された文字列を出力します

    View Slide

  13. YAML問題

    View Slide

  14. 14
    自己紹介
    ● sue445
    ● 2018年7月入社
    ○ もうすぐ4年目
    ● インフラ部所属
    ● 自称YAMLエンジニア
    ○ Ansible, Docker Compose, CircleCI, Travis
    CI, Wercker, GitHub Actions, GitLab CI,
    Serverless Framework, AWS SAM,
    Kubernetes
    sue445

    View Slide

  15. 1. {"a"=>"1"}
    2. {"a"=>1}
    3. シンタックスエラー
    4. その他
    YAML 1問目
    # problem1.yml
    a: 1
    # Rubyで実行
    YAML.load_file("problem1.yml" )

    View Slide

  16. 1. {"a"=>"1"}
    2. {"a"=>1}
    3. シンタックスエラー
    4. その他
    YAML 1問目
    # problem1.yml
    a: 1
    # Rubyで実行
    YAML.load_file("problem1.yml" )

    View Slide

  17. 17
    なぜ?
    ● YAMLの数字っぽい文字列はその言語の数字の型(
    Rubyだと Integer )
    として解釈されます。
    ● 文字列として解釈させたい場合は "1" (ダブルクオーテーション)や
    '1' (シングルクォーテーション)のように囲んでください

    View Slide

  18. 1. "ふたりはプリキュア Splash
    Star,Yes!プリキュア5,Yes!プリ
    キュア5GoGo,フレッシュプリキュ
    ア!"
    2. "ふたりはプリキュア Splash
    Star,,Yes!プリキュア5GoGo,フレッ
    シュプリキュア!"
    3. ",Yes!プリキュア5,Yes!プリキュア
    5GoGo,フレッシュプリキュア!"
    4. シンタックスエラー
    YAML 2問目
    # problem2.yml
    splash: ふたりはプリキュア Splash Star
    yes: Yes!プリキュア5
    yes_gogo: Yes!プリキュア5GoGo
    fresh: フレッシュプリキュア!
    # Rubyで実行
    data = YAML.load_file("problem2.yml" )
    "#{data['splash']},
    #{data['yes']},
    #{data['yes_gogo']}
    ,#{data['fresh']}"

    View Slide

  19. 1. "ふたりはプリキュア Splash
    Star,Yes!プリキュア5,Yes!プリ
    キュア5GoGo,フレッシュプリキュ
    ア!"
    2. "ふたりはプリキュア Splash
    Star,,Yes!プリキュア5GoGo,フレッ
    シュプリキュア!"
    3. ",Yes!プリキュア5,Yes!プリキュア
    5GoGo,フレッシュプリキュア!"
    4. シンタックスエラー
    YAML 2問目
    # problem2.yml
    splash: ふたりはプリキュア Splash Star
    yes: Yes!プリキュア5
    yes_gogo: Yes!プリキュア5GoGo
    fresh: フレッシュプリキュア!
    # Rubyで実行
    data = YAML.load_file("problem2.yml" )
    "#{data['splash']},
    #{data['yes']},
    #{data['yes_gogo']}
    ,#{data['fresh']}"

    View Slide

  20. 20
    なぜ?
    ● true, false, yes, no, on, off はダブルクオーテーションなどで囲まない限り
    YAMLでは全て 真偽値 として扱われます
    ● Hashのvalueだけでなくkeyに対しても同様に適用されます
    data = YAML.load_file("problem2.yml")
    => {"splash"=>"ふたりはプリキュア Splash Star", true=>"Yes!プリキュア5GoGo!",
    "yes_gogo"=>"Yes!プリキュア5GoGo", "fresh"=>"フレッシュプリキュア!"}

    View Slide

  21. 1. {"slack"=>{"webhook_url"=>
    "https://example.com/",
    "channel"=>"random"}}
    2. {"slack"=>{"webhook_url"=>
    "https://example.com/",
    "channel"=>"production_notify"}}
    3. {"slack"=>{"channel"=>
    "production_notify"}}
    4. シンタックスエラー
    YAML 3問目
    # problem3.yml
    default: &default
    slack:
    webhook_url: "https://example.com/"
    channel: "random"
    production:
    <slack:
    channel: "production_notify"
    # Rubyで実行
    data = YAML.load_file("problem3.yml" )
    data["production"]

    View Slide

  22. 1. {"slack"=>{"webhook_url"=>
    "https://example.com/",
    "channel"=>"random"}}
    2. {"slack"=>{"webhook_url"=>
    "https://example.com/",
    "channel"=>"production_notify"}}
    3. {"slack"=>{"channel"=>
    "production_notify"}}
    4. シンタックスエラー
    YAML 3問目
    # problem3.yml
    default: &default
    slack:
    webhook_url: "https://example.com/"
    channel: "random"
    production:
    <slack:
    channel: "production_notify"
    # Rubyで実行
    data = YAML.load_file("problem3.yml" )
    data["production"]

    View Slide

  23. 23
    なぜ?
    ● & (アンカー)と * (エイリアス)で定義済みの値をいい感じに共通化できる
    のはYAMLのよくあるリファクタリング手法ですが、
    <はなく第1要素だけを上書きするマージ(代入に近い)なので、
    今回の場合 default の内容が打ち消されます。

    View Slide

  24. 1. [1.9, 1.10, 1.11, 1.12]
    2. [1.9, 1.1, 1.11, 1.12]
    3. [1.90, 1.10, 1.11, 1.12]
    4. シンタックスエラー
    YAML 4問目
    # problem4.yml
    go:
    - 1.9
    - 1.10
    - 1.11
    - 1.12
    # Rubyで実行
    data = YAML.load_file("problem4.yml" )
    data["go"]

    View Slide

  25. 1. [1.9, 1.10, 1.11, 1.12]
    2. [1.9, 1.1, 1.11, 1.12]
    3. [1.90, 1.10, 1.11, 1.12]
    4. シンタックスエラー
    YAML 4問目
    # problem4.yml
    go:
    - 1.9
    - 1.10
    - 1.11
    - 1.12
    # Rubyで実行
    data = YAML.load_file("problem4.yml" )
    data["go"]

    View Slide

  26. 26
    なぜ?
    ● クオーテーションで囲んでいないので小数として解釈されるため
    1.10 は 1.1 として解釈されます
    ● 厳密に 1.10 として評価するには "1.10" のように囲む必要があります。
    ● 余談ですがGo 1.10が出た時に .travis.yml に 1.10 を追加したらGo 1.1で
    CIが実行されてビルドが失敗したことがあります。

    View Slide

  27. 1. aのみシンタックスエラー
    2. bのみシンタックスエラー
    3. aとb両方シンタックスエラー
    4. シンタックスエラーは無い
    YAML 5問目
    # この中でシンタックスエラーになるのは?
    # a
    excludes:
    - *_test.rb
    # b
    excludes:
    - test/**

    View Slide

  28. 1. aのみシンタックスエラー
    2. bのみシンタックスエラー
    3. aとb両方シンタックスエラー
    4. シンタックスエラーは無い
    YAML 5問目
    # この中でシンタックスエラーになるのは?
    # a
    excludes:
    - *_test.rb
    # b
    excludes:
    - test/**

    View Slide

  29. 29
    なぜ?
    ● *_test.rb の * が前述のエイリアスとして評価されるのですが、
    対応する _test.rb という名前のエイリアスが存在しないため
    シンタックスエラーになります。
    ● シンタックスエラーにしないためには "*_test.rb" のように囲む必要があります。

    View Slide

  30. PHP問題

    View Slide

  31. 31
    自己紹介
    ● /たっ?どさん/
    ● 2012年11月入社
    ○ 入社前はRubyをやってました
    ○ PHPは入社してからの付き合いです
    ● pixiv運営本部開発支援チーム
    ● Emacs PHP Mode 現行メンテナー
    ○ シンタックス色付け係
    ○ 連休中も正規表現書いてました
    tadsan

    View Slide

  32. 1. Http\Referer が
    出力される
    2. Referer が出力される
    3. object(Referer)#1 (0) {} が
    出力される
    4. 実行時に Class "Referer"
    not found エラーで落ちる
    PHP 1問目
    namespace Http;
    class Referrer
    {
    // impl
    }
    echo Referer::class;

    View Slide

  33. 1. Http\Referer が
    出力される
    2. Referer が出力される
    3. object(Referer)#1 (0) {} が
    出力される
    4. 実行時に Class "Referer"
    not found エラーで落ちる
    PHP 1問目
    namespace Http;
    class Referrer
    {
    // impl
    }
    echo Referer::class;

    View Slide

  34. 34
    なぜ?
    ● PHPの ::class マジック定数はクラス名のFQCN(名前空間付きクラス名)を
    文字列として返します
    ● しかし、そのクラスが定義済みか、ロード可能かなどは
    一切問いません
    ● 未定義だとしても ::class の左側に記述したクラス名を名前空間付きにした
    文字列をそのまま返します
    ● このようなミスの検出にはPHPStanのような静的解析が有効です

    View Slide

  35. 1. $a
    2. $b
    3. $c
    4. $d
    PHP 2問目
    namespace pixiv;
    // これらの変数をPHP 8.0 で
    // $f(...$args) のように呼び出したとき、
    // 関数として呼び出せない変数はどれ?
    // ($argsには適切な引数が格納されている )
    $a = 'printf';
    $b = 'function () { return 1; }';
    $c = 'DateTime::createFromFormat';
    $d = ['DateTime', 'createFromFormat'];

    View Slide

  36. 1. $a
    2. $b
    3. $c
    4. $d
    PHP 2問目
    namespace pixiv;
    // これらの変数をPHP 8.0 で
    // $f(...$args) のように呼び出したとき、
    // 関数として呼び出せない変数はどれ?
    // ($argsには適切な引数が格納されている )
    $a = 'printf';
    $b = 'function () { return 1; }';
    $c = 'DateTime::createFromFormat';
    $d = ['DateTime', 'createFromFormat'];

    View Slide

  37. 37
    なぜ?
    ● PHP用語で関数として呼び出し可能な値を
    callable擬似型と呼びます
    ● callableとして型宣言できる値は「関数名の文字列」「クラス名
    ::メソッド名の文字列」
    「Closureオブジェクト」そして「2要素の配列」などです
    ○ 2要素の配列とは [$obj, 'method'] または ['Klass', 'staticmethod']
    ○ 未定義のメソッド名が渡された場合はきちんと弾けます
    ● 言語機能としてClosureが実装されるPHP 5.3より前はcreate_function()という
    関数で引数に文字列を渡すと、擬似的な無名関数が作れました

    View Slide

  38. 1. CONST
    2. CONST_2
    3. CONST_3
    4. CONST_4
    PHP 3問目
    namespace pixiv;
    // 以下の const で定義する定数定義のうち、
    // Syntax Errorにならず問題なく定義できる
    // 定義文の行はどれ?
    // (実際には不正な行を削除しないと実行不能 )
    \define('DDD', 'Defined Constant');
    const CONST = DDD;
    const CONST_2 = 'Con' . 'st';
    const CONST_3 = \strtolower('CONST');
    const CONST_4 = \constant('PHP_EOL');
    ヒント:
    define()関数は実行時(関数呼び出し時)に、
    const文はコンパイル時に定数を定義します。
    . は文字列結合演算子
    strtolower()は文字列を小文字にする関数
    constant()は定義済み定数の値を得る関数

    View Slide

  39. 1. CONST
    2. CONST_2
    3. CONST_3
    4. CONST_4
    PHP 3問目
    ヒント:
    define()関数は実行時(関数呼び出し時)に、
    const文はコンパイル時に定数を定義します。
    . は文字列結合演算子
    strtolower()は文字列を小文字にする関数
    constant()は定義済み定数の値を得る関数
    namespace pixiv;
    // 以下の const で定義する定数定義のうち、
    // Syntax Errorにならず問題なく定義できる
    // 定義文の行はどれ?
    // (実際には不正な行を削除しないと実行不能 )
    \define('DDD', 'Defined Constant');
    const CONST = DDD;
    const CONST_2 = 'Con' . 'st';
    const CONST_3 = \strtolower('CONST');
    const CONST_4 = \constant('PHP_EOL');

    View Slide

  40. 40
    なぜ?
    ● const文に書けるのは定数式と呼ばれるもので、定数およびリテラル
    (配列含む)および、
    それらを組み合わせた演算子式です。引数のデフォルト値と同じ。
    ● 1.はひっかけです。PHPの予約語はケースインセンシティブなので、大文字
    小文字問わずconstという定数・関数は定義できません。
    (変数とメソッドは可)
    ○ define()で定義される定数をconstから参照するのは可能です
    ● 2.はリテラル同士の演算子式なので合法です。
    ● 3., 4. は実行時に評価される関数なので
    const文には書けません。
    PHPにはC++のconst exprのような概念はいまのところありません。

    View Slide

  41. 1. Aが出力される
    2. Bが出力される
    3. 実行時エラーで落ちる
    4. syntax errorで実行できない
    PHP 4問目
    namespace pixiv;
    // 以下のコードを実行するとどうなるでしょうか
    class A {
    public function __toString()
    {
    return B::class;
    }
    }
    class B { }
    $a = new A;
    $obj = new $a;
    echo get_class($obj);

    View Slide

  42. 1. Aが出力される
    2. Bが出力される
    3. 実行時エラーで落ちる
    4. syntax errorで実行できない
    PHP 4問目
    namespace pixiv;
    // 以下のコードを実行するとどうなるでしょうか
    class A {
    public function __toString()
    {
    return B::class;
    }
    }
    class B { }
    $a = new A;
    $obj = new $a;
    echo get_class($obj);

    View Slide

  43. 43
    なぜ?
    ● ここまで関数がそうだったように、
    newにも変数が使えます
    ● $class = 'B'; $obj = new $class; のように
    文字列 'B' を渡すと、Bのインスタンスに初期化になります
    ● newの右辺の変数が文字列ではなくインスタンスオブジェクトだった場合、
    そのインスタンスのクラスとして初期化が実行されます
    ○ この文脈では __toString() の有無は影響を及ぼさず、ひっかけです
    ○ echoなどでは暗黙的に呼び出されますが、
    newは呼び出しません

    View Slide

  44. ゲスト問題
    uzulla

    View Slide

  45. 1. hiと出力される
    2. hihiと出力される
    3. hihihihi…と無限ループする
    4. レスポンスが返ってこない
    PHP 5問目
    // $ php -S 127.0.0.1:8080
    // でビルトインWebサーバを起動し、
    // ブラウザから実行するとどうなるか?
    ini_set("display_errors", 0);
    echo "hi";
    $code = file_get_contents(__FILE__);
    eval(ltrim($code, "

    View Slide

  46. 1. hiと出力される
    2. hihiと出力される
    3. hihihihi…と無限ループする
    4. レスポンスが返ってこない
    PHP 5問目
    // $ php -S 127.0.0.1:8080
    // でビルトインWebサーバを起動し、
    // ブラウザから実行するとどうなるか?
    ini_set("display_errors", 0);
    echo "hi";
    $code = file_get_contents(__FILE__);
    eval(ltrim($code, "

    View Slide

  47. 47
    なぜ?
    ● 通常、__FILE__マジック定数はファイル名の文字列として展開されます
    ● eval 内で展開される二度目の__FILE__の文字列
    "/path/eval.php(4) : eval()'d code" のような形式で、
    ファイル名ではなくなります
    ● このコードは初回起動と一回の
    evalでそれぞれ hi と出力され、
    それ以上は再帰することなく終了します
    ● エラー出力は ini_set("display_errors", 0); で抑制され、最終出力は hihi です

    View Slide

  48. Ruby問題

    View Slide

  49. 49
    自己紹介
    ● usa (unak)
    ● 2018年3月入社
    ● 配信技術部エンジニアマネージャー
    ● Rubyコミッタ
    ○ 毎年Rubyの安定版を殺す係の人
    ○ https://www.ruby-lang.org/ で一番多くの記
    事を書いてる人
    ○ x64版Windowsへの移植をした人
    usa

    View Slide

  50. 1. nil と改行が出力される
    2. nil だけ出て無改行
    3. 改行のみが出力される
    4. 改行も含めて一切何も出力さ
    れない
    Ruby 1問目
    # 以下のスクリプトを実行すると何が
    # 起きますか?
    p

    View Slide

  51. 1. nil と改行が出力される
    2. nil だけ出て無改行
    3. 改行のみが出力される
    4. 改行も含めて一切何も出力さ
    れない
    Ruby 1問目
    # 以下のスクリプトを実行すると何が
    # 起きますか?
    p

    View Slide

  52. 52
    なぜ?
    ● 特に解説するほどでもないですが、
    p は無引数の場合は何も出力しません。

    View Slide

  53. 1. 改行のみが出力される
    2. 何も出力されない
    3. 42 が出力される
    4. SystemStackError 例外が発
    生する
    Ruby 2問目
    # 以下のスクリプトを実行すると何が
    # 起きますか?
    def p(p=42)
    p
    end
    p p

    View Slide

  54. 1. 改行のみが出力される
    2. 何も出力されない
    3. 42 が出力される
    4. SystemStackError 例外が発
    生する
    Ruby 2問目
    # 以下のスクリプトを実行すると何が
    # 起きますか?
    def p(p=42)
    p
    end
    p p

    View Slide

  55. 55
    なぜ?
    ● スクリプト内で再定義された p メソッド内で使われている p は仮引数の p なので、このメ
    ソッドは無引数で呼ばれた場合には単に 42 を返すだけのメソッドとなります。
    ● 括弧があるか引数の指定があればここで自分を再帰呼び出しするんですが、この場合は
    メソッド呼び出しではなく変数参照が優先されるんですね。
    ● で、p p では、まず後ろの p の呼び出しが発生し、無引数なので 42 を返します。

    View Slide

  56. 56
    なぜ?
    ● つづいて、この 42 を引数として再び p が呼ばれますが、今度はこの引数である 42 を返
    すだけです。
    ● 結局、組み込みの p は呼ばれませんので、特になんの出力も行われません。

    View Slide

  57. 1. Bは a に 42 が入るが、AとCは
    syntax errorになる
    2. AとBは a に 42 が入るが C は
    syntax errorになる
    3. BとCは a に 42 が入るが A は
    syntax errorになる
    4. 全部 a に 42 が入る
    Ruby 3問目
    # Rubyには本当の意味での右代入はないですが、
    # Ruby 3.0からは類似のことが 1行パターン
    # マッチング構文で実現可能となっています。
    # では、Ruby 3.0において、以下のスクリプト

    # A・B・Cについて、正しい答えを選んで
    # ください。
    42 => a # A
    (42) => (a) # B
    [42] => [a] # C

    View Slide

  58. 1. Bは a に 42 が入るが、AとCは
    syntax errorになる
    2. AとBは a に 42 が入るが C は
    syntax errorになる
    3. BとCは a に 42 が入るが A は
    syntax errorになる
    4. 全部 a に 42 が入る
    Ruby 3問目
    # Rubyには本当の意味での右代入はないですが、
    # Ruby 3.0からは類似のことが 1行パターン
    # マッチング構文で実現可能となっています。
    # では、Ruby 3.0において、以下のスクリプト

    # A・B・Cについて、正しい答えを選んで
    # ください。
    42 => a # A
    (42) => (a) # B
    [42] => [a] # C

    View Slide

  59. 59
    なぜ?
    ● 全て1行パターンマッチング構文として正しく、
    a に 42 が入ります。
    ● ただし、Cだけは現状は常時警告が出ます。
    ● 来るRuby 3.1ではこの警告も消えるかもです。
    ● なお、これらは式の値自体は nil になりますのでご注意くださいませ。

    View Slide

  60. 1. {42 => 42} が出力される
    2. syntax errorになる
    3. nil が出力される
    4. 何も出力されない
    Ruby 4問目
    # Ruby 3.0で以下のスクリプトを実行すると
    # 何が起きるでしょう?
    a = b = 42
    p { a => b }

    View Slide

  61. 1. {42 => 42} が出力される
    2. syntax errorになる
    3. nil が出力される
    4. 何も出力されない
    Ruby 4問目
    # Ruby 3.0で以下のスクリプトを実行すると
    # 何が起きるでしょう?
    a = b = 42
    p { a => b }

    View Slide

  62. 62
    なぜ?
    ● { a => b } はハッシュロケット構文なのでHashオブジェクト {42 => 42} になって、それを p
    するから答えは1……ではありません。ここでは { はハッシュ式ではなくブロック引数の始ま
    りとみなされますので、Hashオブジェクトにはなりません。Rubyを書き始めたばかりの人
    がよく踏むやつです。
    ● というわけで、ハッシュ式の中以外ではハッシュロケット => は書けませんので、ここで
    syntax errorになるので答えは2……となりそうな気がしますし、実際、
    Ruby 2.7までではそ
    の通りなのですが、Ruby 3.0では第3問で述べた1行パターンマッチング構文が導入され
    たので、a => b は文法的に正しい式です。

    View Slide

  63. 63
    なぜ?
    ● 従って、これは正しい文なので a => b が評価され、その結果は nil なので nil が出力され
    て答えは3…… となるわけではなくて、実はRubyではブロックを引数として受け取らないメ
    ソッドに敢えてブロックを渡した場合、単にそれを無視するという不思議な性質がありま
    す。構文解析だけはされるので、文法的に書けない式を書くと
    syntax errorにはなるんで
    すけどね。
    ● というわけで、{ a => b } は結局 p メソッドには無視されてしまい、引数がないという扱い
    になるので、第1問と同じように、何も出力されないのでした。

    View Slide

  64. ゲスト問題
    mame

    View Slide

  65. 1. gsub!
    2. sub
    3. =
    4. !
    Ruby 5問目
    # 次のコードに"pixiv!"と出力させるには、
    # [] のあたりに選択肢のどれを書き込めばよ
    い?
    str = "p!xiv!"
    str. [] (/!/, 'i')
    puts str

    View Slide

  66. 1. gsub!
    2. sub
    3. =
    4. !
    Ruby 5問目
    # 次のコードに"pixiv!"と出力させるには、
    # [] のあたりに選択肢のどれを書き込めばよ
    い?
    str = "p!xiv!"
    str. [] (/!/, 'i')
    puts str

    View Slide

  67. 67
    なぜ?
    ● ! を両方置き換えたらまずいから gsub! はダメだというのはみなさんすぐにわかると思い
    ますが、sub では元の文字列は変更されないし、どういうことなの? ってなったと思いま
    す。
    ● 実は、「[] のあたりに」というのが罠で、[] 部分を置き換えるのではなくて [] の後ろに = を
    入れるのが正解です。

    View Slide

  68. 68
    なぜ?
    ● String#[]= は、引数が整数であればそこで指定されたインデックスの文字を
    置き換え、整数の範囲式であればその指定された範囲の部分文字列を置き換え
    ますが、引数が正規表現の場合は最初にマッチした部分文字列を置き換えます。
    ● このマイナーな挙動に加えて、それを代入式記法
    (str[/!/] = 'i')ではなく
    メソッド式記法(str.[]=(/!/, 'i'))で使わせるという、普段書かないので
    頭に浮かびにくいコードとなっており、罠が何重にもかけられているという
    問題でした。

    View Slide

  69. JavaScript問題

    View Slide

  70. 70
    自己紹介
    ● moriken (petamoriken)
    ● 2017年5月入社(アルバイト)
    ● プロダクト支援本部 課題解決部 エンジニア
    ○ 福岡オフィス
    ● JavaScript が好きで TC39 会議を追っています
    ○ 「moriken scrapbox」で検索!
    ○ 2018年くらいから TC39 会議の流れが日本語
    で読めます(最近サボリ気味)
    moriken

    View Slide

  71. 1. "PIXIV DEV MEETUP 2021"
    2. "PIXIV undefined MEETUP 2021"
    3. "PIXIV DEV [object String] 2021"
    4. "PIXIV undefined [object String]
    2021"
    JavaScript 1問目
    // 最後の式の結果は?
    const str1 = "PIXIV";
    str1.word = "DEV";
    const str2 = new String("MEETUP");
    str2.word = "2021";
    `${str1} ${str1.word} ${str2} ${str2.word}`;

    View Slide

  72. JavaScript 1問目
    // 最後の式の結果は?
    const str1 = "PIXIV";
    str1.word = "DEV";
    const str2 = new String("MEETUP");
    str2.word = "2021";
    `${str1} ${str1.word} ${str2} ${str2.word}`;
    1. "PIXIV DEV MEETUP 2021"
    2. "PIXIV undefined MEETUP 2021"
    3. "PIXIV DEV [object String] 2021"
    4. "PIXIV undefined [object String]
    2021"

    View Slide

  73. 73
    なぜ?
    ● プリミティブ値に対してプロパティを設定することはできません。
    ● str1 はプリミティブで str2 はプリミティブラッパーオブジェクトなので、
    str2 にのみプロパティを設定できます。
    ● また Object.prototype.toString.call(str2) は "[object String]" を返すのですが
    これはひっかけで、str2.toString() の結果と同じ "MEETUP" の文字列が含まれる
    形になります。

    View Slide

  74. 1. [5, 10, 15, "10"]
    2. [5, 10, "10", 15]
    3. [10, "10", 15, 5]
    4. 実装依存
    (一意に定まらない)
    JavaScript 2問目
    // ソートの結果は?
    [10, 15, "10", 5].sort();

    View Slide

  75. 1. [5, 10, 15, "10"]
    2. [5, 10, "10", 15]
    3. [10, "10", 15, 5]
    4. 実装依存
    (一意に定まらない)
    // ソートの結果は?
    [10, 15, "10", 5].sort();
    JavaScript 2問目

    View Slide

  76. 76
    なぜ?
    ● Array.prototype.sort のデフォルトソートは何故か文字列の比較となっている
    ため、文字列化した際の辞書順に並びます。
    ● Array.prototype.sort は安定ソートとは限らないため実装依存と思った方もいらっしゃる
    かもしれません(数値の 10 と文字列の "10" のどっちが先に来るかわからない)が、これ
    については ES2019 から安定ソートが必須化されています。
    ○ 2, 3年くらい前の古い V8 エンジンの環境でない限り安定ソートだと考えて
    問題ありません。

    View Slide

  77. 1. PIXIV { DEV: "MEETUP" }
    (PIXIV オブジェクト)
    2. "2021"
    3. String { "2021" }
    (プリミティブラッパーオブ
    ジェクト)
    4. throws TypeError
    // 最後の式の結果は?
    class PIXIV {
    constructor() {
    this.DEV = "MEETUP";
    return "2021";
    }
    }
    new PIXIV();
    JavaScript 3問目

    View Slide

  78. // 最後の式の結果は?
    class PIXIV {
    constructor() {
    this.DEV = "MEETUP";
    return "2021";
    }
    }
    new PIXIV();
    1. PIXIV { DEV: "MEETUP" }
    (PIXIV オブジェクト)
    2. "2021"
    3. String { "2021" }
    (プリミティブラッパーオブ
    ジェクト)
    4. throws TypeError
    JavaScript 3問目

    View Slide

  79. 79
    なぜ?
    ● コンストラクタの返り値がオブジェクトの場合はそれで上書きできますが、
    プリミティブ値の場合は上書きできずにそのままスルーされて this が返ります。

    View Slide

  80. 1. { value: "PIXIV", done: false }
    2. { value: "MEETUP", done: false }
    3. { value: undefined, done: true }
    4. throws TypeError
    // 最後の式の結果は?
    function* gen() {
    yield* ["PIXIV", "DEV", "MEETUP", "2021"];
    }
    const iter = gen();
    for (const word of iter) {
    if (word === "DEV") {
    break;
    }
    }
    iter.next();
    JavaScript 4問目

    View Slide

  81. 1. { value: "PIXIV", done: false }
    2. { value: "MEETUP", done: false }
    3. { value: undefined, done: true }
    4. throws TypeError
    // 最後の式の結果は?
    function* gen() {
    yield* ["PIXIV", "DEV", "MEETUP", "2021"];
    }
    const iter = gen();
    for (const word of iter) {
    if (word === "DEV") {
    break;
    }
    }
    iter.next();
    JavaScript 4問目

    View Slide

  82. 82
    なぜ?
    ● この問題は for-of とジェネレーター函数について問われた問題です。
    ○ for-of の途中でループから抜ける場合、イテレーターに return メソッドが定義されて
    いる場合はそれを実行します。
    ○ ジェネレーター函数から作ったイテレーターにはプロトタイプに return メソッドが定義
    されています。実行すると列挙が終わった扱いになります。
    ● 余談ですが ["PIXIV", "DEV", "MEETUP", "2021"].values() で ArrayIterator を作った場合
    は、プロトタイプに return メソッドが定義されていないため結果が変わります。

    View Slide

  83. ゲスト問題
    gaogao_9

    View Slide

  84. 1. "PIXIV"
    2. "DEV"
    3. "MEETUP"
    4. "2021"
    // 最後の式の結果は?
    const obj = Object.create(null, {
    [Symbol("PIXIV")]:
    { value: "PIXIV", enumerable: true },
    DEV:
    { value: "DEV", enumerable: true },
    MEETUP:
    { value: "MEETUP", enumerable: true },
    2021:
    { value: "2021", enumerable: true },
    });
    Object.values(obj)[ Object.keys(obj).length-1 ];
    JavaScript 5問目

    View Slide

  85. 1. "PIXIV"
    2. "DEV"
    3. "MEETUP"
    4. "2021"
    // 最後の式の結果は?
    const obj = Object.create(null, {
    [Symbol("PIXIV")]:
    { value: "PIXIV", enumerable: true },
    DEV:
    { value: "DEV", enumerable: true },
    MEETUP:
    { value: "MEETUP", enumerable: true },
    2021:
    { value: "2021", enumerable: true },
    });
    Object.values(obj)[ Object.keys(obj).length-1 ];
    JavaScript 5問目

    View Slide

  86. 86
    なぜ?
    ● これは Object.keys と Object.values がどんな配列を返すのかを問う問題です。
    ○ 両方ともオブジェクトのプロパティのキーが文字列なもののみを含みます(シンボルは
    含まれません)。
    ● 列挙順は以下の通りです。
    ○ まず配列のキーになり得る数値の文字列を小さい方から列挙します。
    ○ それ以外の文字列はオブジェクトのキーの定義順に返します。
    ● よって ["2021", "DEV", "MEETUP"] のうちの最後の要素が答えです。

    View Slide

  87. Java問題

    View Slide

  88. 88
    自己紹介
    ● orekyuu
    ● 2017年4月新卒入社
    ● BOOTH部所属
    ● Javaが好きなRailsエンジニア
    ○ 業務のちょっとしたスクリプトでも
    Javaで書き始める書くおじさん
    ○ 好きなIDEはIntelliJ
    orekyuu

    View Slide

  89. 1. false/false/false/false
    2. true/false/true/true
    3. true/true/true/false
    4. true/false/true/false
    Java 1問目
    void hoge() {
    String literalJava = "Java";
    final String finalJava = "Java";
    final String newJava = new String("Java");
    System.out.print("Hello, Java" ==
    "Hello, Java");
    System.out.print("/");
    System.out.print(("Hello, " + literalJava) ==
    "Hello, Java");
    System.out.print("/");
    System.out.print(("Hello, " + finalJava) ==
    "Hello, Java");
    System.out.print("/");
    System.out.print(("Hello, " + newJava) ==
    "Hello, Java");
    }
    hoge();

    View Slide

  90. 1. false/false/false/false
    2. true/false/true/true
    3. true/true/true/false
    4. true/false/true/false
    Java 1問目
    void hoge() {
    String literalJava = "Java";
    final String finalJava = "Java";
    final String newJava = new String("Java");
    System.out.print("Hello, Java" ==
    "Hello, Java");
    System.out.print("/");
    System.out.print(("Hello, " + literalJava) ==
    "Hello, Java");
    System.out.print("/");
    System.out.print(("Hello, " + finalJava) ==
    "Hello, Java");
    System.out.print("/");
    System.out.print(("Hello, " + newJava) ==
    "Hello, Java");
    }
    hoge();

    View Slide

  91. 91
    なぜ?
    ● 同じ文字列のリテラルはプールされるため、参照が等しくなります
    (1つ目)
    ● literalJavaとつないだときは新しく文字列が作られるため違うインスタンスになります
    (2つ
    目)
    ● finalで宣言されたStringはコンパイル時にインライン化されるため、
    "Hello, Java"と同等
    になるので等しいです(3つ目)
    ● newされた場合はプールされないため別のインスタンスになります
    (4つ目)
    ● Javaの文字列は==で比較せずにequalsメソッドを使って比較しろってことです
    😡

    View Slide

  92. 1. true
    2. false
    3. 環境によって異なる
    4. 実行時エラー
    Java 2問目
    URL hoge = new
    URL("http://hoge:[email protected]" );
    URL fuga = new
    URL("http://fuga:[email protected]" );
    System.out.println(hoge.equals(fuga)) ;

    View Slide

  93. 1. true
    2. false
    3. 環境によって異なる
    4. 実行時エラー
    Java 2問目
    URL hoge = new
    URL("http://hoge:[email protected]" );
    URL fuga = new
    URL("http://fuga:[email protected]" );
    System.out.println(hoge.equals(fuga)) ;

    View Slide

  94. 94
    なぜ?
    ● URLのequalsはオーバーライドされていて次の条件に一致する場合
    trueを返します
    ○ 同じプロトコルを持ち
    ○ 同じホストを参照し
    ○ 同じホストとは同じIPに解決される
    ● 名前解決できない場合は大文字小文字に関係なくホスト名が等しいか
    ● ホスト上のポート番号が同じでファイルとファイルのフラグメントが同じ場合
    ● ユーザー名の情報は 等価性に含まれない のでtrueになります

    View Slide

  95. 1. main.showText();の行で
    コンパイルエラー
    2. void showText(Main
    Main.this)の行で
    コンパイルエラー
    3. System.out.printlnの行で
    コンパイルエラー
    4. pixivが出力される
    Java 3問目
    public class Main {
    String text;
    public static void main(String[] args)
    {
    Main main = new Main();
    main.text = "pixiv";
    main.showText() ;
    }
    void showText(Main Main.this) {
    System. out.println(Main. this.text);
    }
    }

    View Slide

  96. 1. main.showText();の行で
    コンパイルエラー
    2. void showText(Main
    Main.this)の行で
    コンパイルエラー
    3. System.out.printlnの行で
    コンパイルエラー
    4. pixivが出力される
    Java 3問目
    public class Main {
    String text;
    public static void main(String[] args)
    {
    Main main = new Main();
    main.text = "pixiv";
    main.showText() ;
    }
    void showText(Main Main.this) {
    System. out.println(Main. this.text);
    }
    }

    View Slide

  97. 97
    なぜ?
    ● かなりマイナーな構文ですが、非
    staticなメソッドではReceiver parameterと呼ばれるも
    のを定義できます
    ● Receiver parameterを受け取ってなにかの情報を取ることはできないので無意味なよう
    に見える構文です
    ● 嬉しさとしてはここにアノテーションを書くことができるので、コンパイル時に
    Receiverに対
    して何かしらのチェックを走らせたいときに使うらしいです

    View Slide

  98. 1. 常にtrue
    2. 常にfalse
    3. 実行ごとに結果が変わる
    4. 実行時エラー
    Java 4問目
    boolean prev = true;
    int matchCount = 0;
    for (int i = 0; i < 100; i++) {
    boolean other =
    new Random(System.currentTimeMillis())
    .nextBoolean()
    ;
    if (prev == other) matchCount++
    ;
    prev = other;
    }
    System.out.println(matchCount > 50);

    View Slide

  99. 1. 常にtrue
    2. 常にfalse
    3. 実行ごとに結果が変わる
    4. 実行時エラー
    Java 4問目
    boolean prev = true;
    int matchCount = 0;
    for (int i = 0; i < 100; i++) {
    boolean other =
    new Random(System.currentTimeMillis())
    .nextBoolean()
    ;
    if (prev == other) matchCount++
    ;
    prev = other;
    }
    System.out.println(matchCount > 50);

    View Slide

  100. 100
    なぜ?
    ● よくよく見るとRandomのシード値がミリ秒になっています
    ● 現代のマシンでは1ミリ秒の間にループがすべて回ってしまうため同じシード値になってし
    まう
    ● Randomは使い回さない + デフォルトコンストラクタを使いましょう

    View Slide

  101. ゲスト問題
    noko_k

    View Slide

  102. 1. 実行時例外
    2. Hello null
    3. 何も出力されない
    4. それ以外
    Java 5問目
    class Words {
    static String HELLO = "Hello";
    static String WORLD = "World";
    }
    Words words = new Words();
    "%s %s".formatted(words. HELLO,
    ((Words) null).WORLD)
    .chars().forEach(System. out::print);

    View Slide

  103. 1. 実行時例外
    2. Hello null
    3. 何も出力されない
    4. それ以外
    72101108108111328711111
    4108100が出力されます
    Java 5問目
    class Words {
    static String HELLO = "Hello";
    static String WORLD = "World";
    }
    Words words = new Words();
    "%s %s".formatted(words. HELLO,
    ((Words) null).WORLD)
    .chars().forEach(System. out::print);

    View Slide

  104. 104
    なぜ?
    ● ((Words) null).WORLD の部分については、NullPointerExceptionとなりません。static
    フィールドの呼び出しは、参照するインスタンスが
    nullかどうかに関わらず参照することが
    出来ます(つまりこの式は"World"になります)
    ● chars() メソッドは文字列をIntStreamに変換します(CharStreamは存在しません)
    ● IntStream#forEachはIntConsumerを受け取ります。それによりここで呼び出されるメ
    ソッドは引数がintのprintメソッドとなります。
    ● これにより、実行するとそれぞれの文字を整数表現にした数字がそれぞれ表示されてし
    まいます。

    View Slide

  105. 結果発表!

    View Slide