本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーはkarupaneruraこと佐藤健太さんで、
本稿のサンプルコードは、
短くシンプルにコードを書き上げるテクニック
みなさんもご存じのとおり、
このとき、
そんな場面においては、
そこで今回は、
まず
特殊変数$_を使う
Perlの特色の一つとして、$_の存在が挙げられるでしょう。この特殊変数の役割は大きく分けて2つあります。一つは、foreachのループ値の変数の指定を省略した際に$_へ自動的にループ値が代入されるような、ucなどの組込み関数のデフォルトの引数としての役割です。
$_は暗黙的に使われるものであるため、$_をやみくもに使うと、$_を使うと、$_なのか見分けが付きにくく、
しかし、mapやgrepなど処理する対象が自明である場合にも便利です。$_の使いどころを考えるヒントを表1にまとめました。
| 検討事項 | 使ってもよい場面 | 使うべきでない場面 | 
|---|---|---|
| 必要な事前処理 | 少ない | 多い | 
| 扱うデータの種類 | 少ない | 多い | 
| 処理の内容 | 単純 | 複雑 | 
| 処理の対象 | 自明 | 自明ではない | 
ワンライナーで使う
$_を便利に活用できる代表的なユースケースとして、
次のコードは、
$ perl -e 'print "Hello, World!\n"'
ちょっとした問題を手早く片付ける際に、grepやsedなどのUNIXコマンドだけでは複雑になる場面でも、
そして、$_は非常に便利に使えます。たとえば、
$ perl -pe '$_=uc'
処理系perlの-pオプションを使用することで、
B::Deparseを用いて展開したコード
LINE: while (defined($_ = readline ARGV)) {
    $_ = uc $_;
}
continue {
    die "-p destination: $!\n" unless print $_;
}引数に指定したコードがループの中に展開されます。これは、continueセクションでprintするしくみです。そして、ucの引数が、$_に補完されています。
別の例として、sedの代替としてperlを使って、
$ perl -pe 's/foo/bar/g'
この例も、=~を利用せずに正規表現による置換を行うため、$_が処理の対象になります。結果として、sedのような処理をこれだけで実現できます。
このように、
ちなみに、perlにはほかにも-nや-aなどさまざまなコマンドラインオプションが実装されています。それぞれのオプションが問題にはまれば、
foreachと組み合わせて使う
一般的にforeachはループを書くときに使われますが、$_を代入するためだけに使うこともできます。なお、foreachはforと等価ですので、forとして説明します。
次の例では、$fizzbuzz_がfizzかbuzzを含む場合に文字列を出力します。
say "$fizzbuzz_text includes fizz"
    if $fizzbuzz_text =~ /fizz/;
say "$fizzbuzz_text includes buzz"
    if $fizzbuzz_text =~ /buzz/;十分わかりやすいですが、$fizzbuzz_という名前は長く見通しが悪いです。かといって、
forを使って書きなおすと、
for ($fizzbuzz_text) {
    say "$_ includes fizz" if /fizz/;
    say "$_ includes buzz" if /buzz/;
}forは通常リストに対してループを行いますが、$_が使われます。結果的にこのforのブロックは、$_を$fizzbuzz_として扱うブロックとして使えます。そして、=~演算子を利用せずに正規表現マッチを行っているため、$_がその処理の対象になります。
しくみは少し複雑ですが、
入出力のフォーマットをコントロールする
Perlはテキスト処理に特化して作られた歴史から、
入力の行セパレータを変更する
最もよく使われるものは、$/です。Perlには、<>演算子や組込み関数のreadlineなど、\n)
この特殊変数の値を変更すれば、\r\n)$/に\r\nを設定して読み込むことで自然と処理できます。
純粋に入力の行セパレータを変更する目的でも$/を利用できますが、File::SlurpやPath::Tinyのslurpメソッドなどがその実装として存在するように、slurpと呼ばれるのが一般的です。
slurpを$/を使わずに素朴に実装すると、
my $all_of_texts = '';
for my $line (<$fh>) {
    $all_of_texts .= $line;
}$/を使えば、
$/ = undef;
my $all_of_texts = <$fh>;$/をundefとするとPerlはファイルの終端までを1行として読み込みます。これによって、slurp相当の処理を簡単に実現できます。
なお、localを使ったダイナミックスコープで局所化して行うことが一般的です。
my $all_of_texts = do {
    # 初期化せず局所化するとundefになる
    local $/;
    <$fh>;
};ほかの特殊変数を扱う場合でも、localで局所化して扱うとよいでしょう。
出力の行・列セパレータを変更する 
出力においても、printです。デフォルトでは出力のセパレータは規定されず、printを連続して呼び出してもprintの引数の内容が単に続けて出力されます。
# "helloworld"と出力される
print "hello";
print "world";printは一般的には1つの引数の例しか示されませんが、
# "helloworld"と出力される
print "hello", "world";これも同様にデフォルトのセパレータは規定されず、
ここでは便宜上、printの呼び出しごとに末尾に付くセパレータを行セパレータ、printの引数の間に付くセパレータを列セパレータとして説明します。
行セパレータを変更するためには、$\を使います。使い方は先ほど説明した$/と同様です。
local $\ = "\n";
# "hello\nworld\n"と出力される
print "hello";
print "world";列セパレータを変更するためには、$,を使います。これも使い方は同様です。
これらを組み合わせると、
local $, = ",";
local $\ = "\n";
print "date", "message";
print "08/22", "too hot!";これは次のように出力されます。
date,message 08/22,too hot!
素朴に実装するにはjoinなどを使う必要がありますが、$,を\tにすれば、
配列を文字列に展開する際のセパレータを変更する
出力の列セパレータとなる特殊変数$,に似ていますが、$"も存在します。これは、
my @arr = qw/foo bar baz/;
# "foo bar baz"と出力される
print "@arr\n";これもさまざまな用途がありますが、
my @result = doit(); # 何かしらの処理
if (is_fail(@result)) { # 結果から失敗を判定
    local $" = ', ';
    die "failed. result: @result";
}この例では、@resultが("a", "b", "c")となり、is_が真になるとき、failed. result: a, b, cというメッセージで例外を発生させます。
<続きの
本誌最新号をチェック!
WEB+DB PRESS Vol.130
2022年8月24日発売
B5判/
定価1,628円
ISBN978-4-297-13000-8
- 特集1
 イミュータブルデータモデルで始める
 実践データモデリング
 業務の複雑さをシンプルに表現!
- 特集2
 いまはじめるFlutter
 iOS/Android両対応アプリを開発してみよう 
- 特集3
 作って学ぶWeb3
 ブロックチェーン、スマートコントラクト、 NFT 


