Ruby 2.4.1新機能: Onigmo正規表現の非包含演算子(?~ )をチェック

こんにちは、hachi8833です。Ruby 2.4.1リリースおめでとうございます。 多くの修正・変更点の中から、私にとって関心の高い非包含演算子を取り急ぎチェックしてみました。結構奥が深そうなので、何かわかったら別途記事にするかもしれません。 「特定の文字列を含まない文字列にマッチする」なんて夢のようじゃありませんか。 参考情報 公式ニュース: Ruby 2.4.1 Released 2.4.0以降のコミット: Comparing changes 公式のコミットはmergeばかり並んでいて読みづらいので、RubyFlowの速報も参照しました。 Ruby 2.4.1 Onigmoアップデートで非包含演算子が導入 Rubyの正規表現エンジンとしておなじみのOnigmoはRubyとは独立してメンテされていますが、6.0.0から6.1.1へのアップデートにともない、ある意味で正規表現の掟破りとも言えるabsent absence operator(非包含演算子)が導入されました。 #82では、田中哲(akr@fsij.org)さんのスライド「正規表現における非包含オペレータの提案」が参考として挙げられています。私の理解では、1.現実の文字セットが有限であり、2.必ず補集合を取れる、ということがこの演算子が成立する根拠であるようです。 非包含演算子のコードを読むのは今夜の楽しみに取っておきますが、Onigmoの非包含演算子(?~正規表現)は同等の既存の正規表現をエイリアスしたものを組み立てて使っているのではないかと現時点で仮説を立てています(追記: 実装はそのようにはなっていませんでした)。 Onigmoの非包含演算子の使い方 上のスライドでは!(正規表現)という演算子を用いていますが、Onigmoでは(?~正規表現)というlook ahead/behind風の演算子表記を採用しています。 以下はスライドを大急ぎで読んだだけで動かして確認した動作なのでご了承ください。実際にはnilが出力されていますが、コード内の出力結果からは取り除いてあります。 # 非包含演算子なしで普通に書いた場合 puts “/*コメントコメント*/”.match(/\/\*[\p{L}\p{P}]+\*\//) #=> /*コメントコメント*/ puts “/*コメント*/コメント*/”.match(/\/\*[\p{L}\p{P}]+\*\//) #=> /*コメント*/コメント*/ # 非包含演算子を使った場合 puts “/*コメントコメント*/”.match(/\/\*(?~\*\/)\*\//) #=> /*コメントコメント*/ puts “/*コメント*/コメント*/”.match(/\/\*(?~\*\/)\*\//) #=> /*コメント*/ 非包含演算子なら”/*コメント*/コメント*/”の場合でも見事に”/*コメント*/”にマッチしています。 なお、最初の2つの正規表現にある[\p{L}\p{P}]+は、.+と似たようなものでして、単なる私の趣味の反映です。私は宗教上の理由から.+や.*のような雑なマッチが嫌いなだけなので、皆さまは.+とか.*を存分にお使いいただいてかまいません。 非包含演算子の中の正規表現は順序が保たれる 上の例では文字クラスによるマッチを使いましたが、非包含演算子のもっともありがたいところは「中に書いた文字列の順序を保って否定マッチできる」という点でしょう。多くの開発者がときたま発作的に欲しくてたまらなくなる「”○☓”という文字列を含まない文字列にマッチする」正規表現を素直に書けるようになります。 puts “うらにはにわにわにはにわにわとりがいる”.match(/(?~にわにはにわ)/) #=> うらにはにわにわにはに puts … Continue reading Ruby 2.4.1新機能: Onigmo正規表現の非包含演算子(?~ )をチェック