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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  5. ルール説明

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  17. 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 full-size 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 full-size slide

  19. 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 full-size slide

  20. 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:
    <<: *default
    slack:
    channel: "production_notify"
    # Rubyで実行
    data = YAML.load_file("problem3.yml" )
    data["production"]

    View full-size 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:
    <<: *default
    slack:
    channel: "production_notify"
    # Rubyで実行
    data = YAML.load_file("problem3.yml" )
    data["production"]

    View full-size slide

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

    View full-size slide

  23. 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  30. 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 full-size slide

  31. 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 full-size slide

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

    View full-size slide

  33. 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 full-size slide

  34. 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 full-size slide

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

    View full-size slide

  36. 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 full-size slide

  37. 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 full-size slide

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

    View full-size slide

  39. 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 full-size slide

  40. 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 full-size slide

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

    View full-size slide

  42. ゲスト問題
    uzulla

    View full-size slide

  43. 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 full-size slide

  44. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  54. 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 full-size slide

  55. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  61. ゲスト問題
    mame

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  66. JavaScript問題

    View full-size slide

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

    View full-size slide

  68. 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 full-size slide

  69. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  74. 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 full-size slide

  75. // 最後の式の結果は?
    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 full-size slide

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

    View full-size slide

  77. 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 full-size slide

  78. 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 full-size slide

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

    View full-size slide

  80. ゲスト問題
    gaogao_9

    View full-size slide

  81. 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 full-size slide

  82. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  85. 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 full-size slide

  86. 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 full-size slide

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

    View full-size slide

  88. 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 full-size slide

  89. 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 full-size slide

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

    View full-size slide

  91. 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 full-size slide

  92. 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 full-size slide

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

    View full-size slide

  94. 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 full-size slide

  95. 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 full-size slide

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

    View full-size slide

  97. ゲスト問題
    noko_k

    View full-size slide

  98. 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 full-size slide

  99. 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 full-size slide

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

    View full-size slide

  101. 結果発表!

    View full-size slide