Slide 1

Slide 1 text

Goの標準パッケージregexpが先読みに対応していな い件 エンジニア達の「完全に理解した」Talk #70 yamatai12 1

Slide 2

Slide 2 text

自己紹介 yamatai12(Webエンジニア) SNS X(taiyama1212) Qiita(yamatai12) Zenn(yamatai12) 趣味は筋トレ プロフィールです、よろしくお願いします 2

Slide 3

Slide 3 text

完全に理解した goの標準ライブラリregexpは先読みに対応していない 先読みのアルゴリズムは指数関数的な実行時間を要する 3

Slide 4

Slide 4 text

背景 顧客の問い合わせ対応でのこと カスタマーサクセス担当者「以下の正規表現で良いでしょうか?」 8(?=2) 私「問題ないです(正規表現の記法も問題ない) 」 4

Slide 5

Slide 5 text

Internal ServerError なぜ?? 5

Slide 6

Slide 6 text

原因 goの標準ライブラリregexpを使っていた regexpは先読みに対応していない 6

Slide 7

Slide 7 text

先読みとは (?=re) (?!re) 例) a(?=..d) aの次に任意の2文字+d が来る場合に限り、aにマッチする 7

Slide 8

Slide 8 text

regexpはRE2の記法に従う https://pkg.go.dev/regexp it is the syntax accepted by RE2 and described at https://golang.org/s/re2syntax RE2は先読みをサポートしていない (?=re) before text matching re (NOT SUPPORTED) (?!re) before text not matching re (NOT SUPPORTED) RE2のアルゴリズムは入力文字列の長さに比例した時間で実行される 8

Slide 9

Slide 9 text

RE2はなぜ先読みに対応していないのか? 理由) 先読みに対応すると検索が遅くなる 先読みは指数関数的な実行時間を要するため RE2 doesn't support lookahead, positive or negative, since it would requires an algorithm with worst-case exponential running time. https://groups.google.com/g/re2j-discuss/c/02Wzz-v2Flo 9

Slide 10

Slide 10 text

なぜ先読みは指数関数的な実行時間を要するのか? a?の二者択一がN回分積み上がる(2^Nパターン) ^(?=(a?)+$)a+$ "aaaaa...a" + "b" (aをN個並べて最後にb) 先読みは「全体が a? を繰り返したもので行末まで到達できるか」を確認する a? は各位置で「取る or 取らない」の2択があるため、長さNのa列には2^N通りの分解 が存在する これを行末$まで到達できる分解を探す 10

Slide 11

Slide 11 text

今後勉強したいこと、課題 先読みのアルゴリズムを図にしてわかりやすい説明をできるようになりたい 11

Slide 12

Slide 12 text

まとめ goの標準ライブラリregexpは先読みに対応していない 先読みのアルゴリズムは指数関数的な実行時間を要する 12

Slide 13

Slide 13 text

参考 https://pkg.go.dev/regexp https://golang.org/s/re2syntax https://qiita.com/ryosuketter/items/bf0d7bfc413ab05723a7 https://www.akenotsuki.com/misc/srell/relibs.html https://swtch.com/~rsc/regexp/regexp1.html https://groups.google.com/g/re2j-discuss/c/02Wzz-v2Flo https://zenn.dev/usamik26/articles/regex-lookahead https://ja.javascript.info/regexp-quantifiers 13