正規表現に関する戯れ言
以下の記事を見て思いついたことを戯れ言として書いておく。 コード少な目でゴメンペコン(どっちのセクションで書こうか悩んだ)。
詳しい解説はブックマークにある記事を参照のこと。
正規表現 regexp パッケージは(概ね)遅い
そもそも正規表現(regular expression)は,それ自体が言語の一種と言える1。 スクリプト言語のように言語仕様の一部として組み込まれている場合は別だが, Go 言語のようなコンパイル言語の場合,普通は(言語本体ではなく)ライブラリやフレームワークの一部として組み込まれる。
Go 言語標準の正規表現エンジンである regexp パッケージは(Perl や Ruby といった言語に比べて)概ね遅い。 これは本当である。 regexp パッケージの設計方針について以下のような記述がある。
まぁぶっちゃけて言うと regexp パッケージは「遅くなりすぎない」ように作られているのだ2。
strings パッケージを使え
その代わりと言ってはナニだが Go 言語では文字列検索や操作のための strings や unicode といった標準パッケージが充実している。
例えば,単純な前方一致や後方一致なら strings
.HasPrefix()
関数や strings
.HasSuffix()
関数を使いやがれ,みたいな感じ。
ただ,正規表現を使うことでコードの可読性が上がる(ことによってバグ混入リスクを減らせる)ことは確かなので,状況によってパッケージを使い分けるのがコツである。
regexp パッケージ利用のコツ
regexp パッケージを使う場合はちょっとした工夫が必要となる。
まず正規表現のコンパイルにはそこそこのコストがかかるため,できるだけ使いまわすようにする。 固定の正規表現ならグローバル変数として初期化してしまうのも手である3。
var re = regexp.MustCompile("^[0-9a-fA-F]+$")
複数の正規表現を動的に切り替えるのであれば map 等を使ってコンパイル結果をキャッシュするのもいいかもしれない。
さらに正規表現による評価を実行する際は排他制御がかかるため,並行処理下でコンパイル済みインスタンスをそのまま使うとパフォーマンスが落ちる。
そこで regexp
.Regexp.Copy()
関数を使ってインスタンスのコピーを生成し,コピーを使って評価を行う。
if !re.Copy().MatchString(s) {
fmt.Println(s, "is not hexadecimal.")
}
その正規表現ホンマにいるの?
いや,便利なのは分かるんだけどさ,「その正規表現ホンマにいるの?」ってのがあるのよ,たまに。 私もやっつけのコードだと正規表現で済ますことがあるけど。
どうしても正規表現が必要で Perl や Ruby といった他言語のほうがシステム全体のパフォーマンスがいいというのであれば,それらの言語を使えばいいと思う。 もしくは正規表現パッケージを自作するか。
個人的に「言語の選択は適材適所」と思ってるので,無理に Go 言語にこだわる必要はないと思う。 もちろん遊びでやるのなら好きにすればいいんだけど。
ブックマーク
- Regular Expression Matching Can Be Simple And Fast
- regexpとの付き合い方 〜 Go言語標準の正規表現ライブラリのパフォーマンスとアルゴリズム〜 | eureka tech blog
- 正規表現チェッカー(JavaScript版) | Softel labs
- 40行以内で正規表現エンジンを構築 | プログラミング | POSTD
- [[Go] パフォーマンスが悪い正規表現パッケージの使い方をチェックするlinterを作った - My External Storage](https://budougumi0617.github.io/2020/08/20/regexponce/) : regexp パッケージ用の linter らしい
参考図書
- プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- Alan A.A. Donovan (著), Brian W. Kernighan (著), 柴田 芳樹 (翻訳)
- 丸善出版 2016-06-20
- 単行本(ソフトカバー)
- 4621300253 (ASIN), 9784621300251 (EAN), 4621300253 (ISBN), 9784621300251 (ISBN)
- 評価
著者のひとりは(あの「バイブル」とも呼ばれる)通称 “K&R” の K のほうである。この本は Go 言語の教科書と言ってもいいだろう。