こんにちは。id:anatofuzです。 これはPerl Advent Calendar 2024の15日目の記事です。
昨日はid:mackee_wさんによるaquaでperlを入れて使えるようになりました でした。relocatable-perl自分は低スペックのPCでとりあえずPerl動かしたいときによく使っています。
さて、PerlはC言語からの影響が強い言語ですが、C言語にあってPerlにない、というよりあったが使われてなかった機能にcase文があります。 かつてはPerlのコアにgiven-whenと呼ばれるcase文の機能がPerl5.10.1から導入されていました。
use v5.10.1; given ($var) { when (/^abc/) { $abc = 1 } when (/^def/) { $def = 1 } when (/^xyz/) { $xyz = 1 } default { $nothing = 1 } }
これはgiven
の後ろに評価したい変数をいれ、when
キーワードでいわゆるcaseに相当するものを書くスタイルです。
一見すると良さそうな機能なのですが、given
で値をスマートマッチ演算子(~~
)を使って評価するという特徴があります。
このスマートマッチ演算子が曲者で、いくつかの問題を抱えていることから5.18で非推奨となりました。
http://perldoc.jp/docs/perl/5.18.0/perl5180delta.pod#The32smartmatch32family32of32features32are32now32experimental
長らく非推奨という形だったのですが、これを受けて5.41.3(Perlの開発用バージョン)でついにスマートマッチ演算子とgiven-whenが削除されました。 次のリリース予定の安定版(Perl5.42またはPerl42)でもスマートマッチ演算子とgiven-whenは削除されてリリースとなります。
ということでコア機能からはcase文は消されたのですが、しかし全ての計算をif
~elsif
で書くのはちょっとだるい、というときが存在します。我々はcase文がやはり欲しくなるときがあるわけです。
さて、最近のCPANモジュールにはSyntax::Keyword
名前空間のモジュールがいくつか存在します。
これはPaul Evans先生がPerlのsyntax plugin 機能を利用し構文の拡張を検証しているモジュール郡です。
PaulEvansはPerlのコア開発者でもあるので、Syntax::Keywordで成果がでた言語機能がPerl本体に取り込まれるというのが最近のムーブメントとなっています。具体的には5.36より導入されたdeferはSyntax::Keyword::Deferの開発内容が元になっています。このためSyntax::Keyword名前空間のモジュールとPerlコアに最近入っている新機能は内容がコンパチであるので、Feature::Compat
名前空間のモジュールを利用するとPerlバージョンに応じてコアかSynrax::Keywordかどちらかで処理を実行するという互換性に強いPerlアプリケーションを書くことができます。例えばdeferはFeature::Compat::Deferを使うとdeferがfeatureにある場合はfeature, ない場合はSyntax::Keywordなモジュールが使われるので、まずアプリケーション側をdefer対応してからPerlのバージョンアップ、というのが比較的スムーズに行えます。
さて、given-whenは消えてしまったわけですが、なんと今CPANにはSyntax::Keyword::Matchモジュールが公開されています。もちろんこれはPaulEvansによって作られた新しいcase文の検証実装です。
use v5.16; use Syntax::Keyword::Match; my $n = ...; match($n : ==) { case(1) { say "It's one" } case(2) { say "It's two" } case(3) { say "It's three" } case(4), case(5) { say "It's four or five" } case if($n < 10) { say "It's less than ten" } default { say "It's something else" } }
主な特徴はmatch
キーワードの後ろに評価したい変数と演算子を指定します。
各case文はその演算子での比較先を書くことができます。この例では$n
を数字として比較演算しているわけですね。
各caseブロックはgolangと同様に自動でbreakされます。cと違い明示的に書かなくても{}
のブロック内で処理を完結できるので直感的ですね。複数の評価値で実行したいブロックをまとめたい場合は,
で繋いで書くことで実装できます。
ここまでだと他の言語のcase文と同じなのですが、おもしろポイントとしてcase
の後ろにif
を書くことで別の評価を行うことができます。上の例ではcase if ($n < 10)
としていて、ここだけ大小演算が行われるわけですね。
他にはmatch構文のスコープだけで有効な変数も定義できます。例えばこの例では$x
はcase文の中だけ使うことができる変数です。
match( my $x = some_function_call() : == ) { case ... }
さてこう見ると結構使えそうな感じがありますね。実はこの前のISUCON14ではこっそりこのcase文を使ってみています。 一応導入前にif-elsifとのベンチマークの比較をしたのですが、ほぼ等価、またはcase文の方が多少早いという結果になったのでパフォーマンスも非常に良いです。 というわけである程度のアプリケーションでも実際にキビキビ動作するcase文、ぜひ使ってみてはどうでしょうか。
明日はid:karupaneruraさんでTBDです。お楽しみに!