読者です 読者をやめる 読者になる 読者になる

三項演算子と関数の罠 "Use of ?PATTERN? without explicit operator is deprecated"

$ # re.plで実行
$ sub FOO { 'FOO' }
$ my $foo = FOO
FOO
$ $foo eq FOO ? 'foo' : 'bar' # NG
Use of ?PATTERN? without explicit operator is deprecated at (eval 410) line 8.
Compile error: Search pattern not terminated or ternary operator parsed as search pattern at (eval 410) line 8.

……えっ、どゆこと!?

"Use of ?PATTERN? without explicit operator is deprecated"

perldocによると*1

Use of ?PATTERN? without explicit operator is deprecated

(D deprecated) 一度だけマッチングする正規表現として ?\w? のようなものを 書きました。 疑問符を将来新しい演算子として利用可能とするために、この単語を直接疑問符 デリミタで始めることは非推奨となりました。 代わりに、明示的に m 演算子を使って m?\w? と書いてください: これも 疑問符デリミタは一度だけマッチングする振る舞いを起動します。

perldiag - さまざまな Perl 診断メッセージ - perldoc.jp

どうやら"?"記号が正規表現のデリミタ(区切り文字)として認識されたようです。

$ 'foobar' =~ /(bar)/; # 普通の場合
bar
$ 'foobar' =~ ?(bar)?; # ?をデリミタとして使った場合(strictだと死亡)
Use of ?PATTERN? without explicit operator is deprecated at (eval 421) line 8.
bar

古いPerlでは"?"で囲うことでも一時的な正規表現を生成できたのですね。perldocを見る限りでは、5.8の時点で既に非推奨で、5.10で廃止されたようです。

?PATTERN?

これは、reset() 演算子を呼び出すごとに 1 度だけしか マッチしないことを除いては /pattern/ による検索と全く同じです。 たとえば、ファイルの集まりの中で個々のファイルについて、 あるものを探すとき、最初の 1 つだけの存在がわかれば良いのであれば、 この機能を使って最適化をはかることができます。 現在のパッケージにローカルとなっている ?? のパターンだけが リセットされます。

perlop - Perl の演算子と優先順位 - perldoc.jp

なぜこのエラーが出るのか

"?PATTERN?"が廃止されたことは分かるのですが、$foo eq FOO ? 'foo' : 'bar'で何故このエラーが出るのでしょう?

三項演算子の"?"が正規表現のデリミタとして解釈されたとしか考えられませんが、それにしても終了デリミタが無いので、正規表現と解釈されようがないように思えるのですが…。

少し実験してみます。

$ $foo eq 'FOO' ? 'foo' : 'bar' # OK
foo
$ $foo eq &FOO ? 'foo' : 'bar' # OK
foo
$ ($foo eq FOO) ? 'foo' : 'bar' # OK
foo
$ FOO eq $foo ? 'foo' : 'bar' #OK
foo

また、FOOにプロトタイプを付けて、完全に定数関数化することでも解決出来ました。

どうも引数を取れるサブルーチンの右に"?"が来ると挙動不審になるようです。

結論

レアケースかもしれませんが、

  • 定数関数であればプロトタイプつけるか、素直にconstant.pm使う。
  • 定数関数でない場合は、プロトタイプつけるのは少し抵抗があるので、比較演算子の左側にサブルーチンを持ってくるのが良さそう。

Perlのソースのlexerあたりを探れば、この問題の根幹が見つかるのかなぁ。

*1:古いバージョン(5.8とか)だとこのエラーメッセージは存在しないみたいです。