本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーは小林謙太さんで、
わかりやすく変更しやすいPerlコードを学ぶ
最初に、
ISUCONは処理速度を競うので、
ISUCONに限らず、
本稿では、
それらの解説に入る前に、
わかりやすさとは意味を理解するまでの認知の円滑さ
まず
本稿では認知心理学の言葉を借りて、
以降では、
意識せずに注目を集めるとわかりやすい
まず情報の知覚に関して、
視覚には
視覚のこの2つの処理の良いところをとって
意識せずに注目を集める方法の一つは、
記憶容量を節約するとわかりやすい
次に、
突然ですが、
また、
これらにより、
意図や行動を連想できるとわかりやすい
最後は、
「さくら」
この特性によって、price *= 1.のようにマジックナンバーを使ったコードが挙げられます。価格を1割増加していることは推測できますが、price *= 1.だけでは情報不足です。意図や行動を連想できるコードのほうが認知負荷は少なくなります。
また、
変更しやすくするために、もとに戻しやすくする 
コードの
少し突飛な質問ですが、
ISUCONの参考実装では、
Perlの4つの新機能
ISUCON11の参考実装ではPerl 5.try/、isa、postderef、signaturesという4つのPerlの新機能を利用しています。これらは、
try/catch ──罠に悩まされる心配をなくす 
ISUCON11でPerl 5.try/を使うためと言っても過言ではありません。
従来のPerlの例外処理では、evalと特殊変数の$@を使っていました。
eval { die "error!!" };
if ($@) {
  # catch error
}ただ、eval、$@を直接使うことはお勧めしません。$@がグローバル変数のため、evalが意図せずクリアされる問題があるためです。local $@で回避はできますが、
eval { die "error!!" };
# このevalで$@がリセットされる
eval { };
if ($@) {
  # no error...
}この問題を回避するデファクトスタンダードの方法は、Try::Tinyを使うことです。ただ、try/の中でのreturnです。次のコードでは、catchでreturnしても、sample_関数の戻り値はSuccessになります。この挙動が直感と反する人はいるでしょう。なお、Try::Tinyのバージョン0.
use Try::Tiny;
sub sample_try {
  try { die }
  catch {
    # このreturnでsample_tryが終わってほしい
    # しかし、終わらない
    return "Fail"
  };
  return "Success"
}
sample_try(); # => SuccessPerl 5.try/がビルトインされ、
use experimental qw(try);
sub sample_try {
  try { die }
  catch ($e) {
    return "Fail"
  } # try/catch構文になったので、末尾のセミコロンが不要
  return "Success"
}
sample_try(); # => Fail補足として、experimentalプラグマは、use feature qw(try)でtry/を有効化しつつ、nowarnings 'experimental::try'をして、experimentalのバージョン0.
isa ──継承をスッキリと調べる
オブジェクトの継承関係を調べるisaオペレータは、
従来は、blessed($o) && $o->isa('Some::Class')と書いていました。継承関係を調べるisaメソッドは、$oがクラス名を指す文字列であっても呼び出せるため、Scalar::Util::blessedでオブジェクトかどうかを確認する必要がありました。
isaオペレータを使うと、$o isa Some::Classと書くだけでオブジェクトの継承関係を調べられます。目的以外のコードが省け、
postderef ──スライスを簡潔にする
postderefは、my ($foo,$bar) = $hash->@{qw/と書けます。これは、$hashから配列@)fooとbarのキーの値を取り出すコードで、
my $arr  = [qw/a b c d e/];
my $hash = { foo => 1, bar => 2, baz => 3 };
# 従来の書き方
my @all         = @$arr;
my ($b, $c, $d) = @$arr[1..3];
my %copy        = %$hash;
my %part        = %$hash{qw/foo/}; # ( foo => 1 )
my ($bar, $baz) = @$hash{qw/bar baz/};
# postderefを利用
my @all         = $arr->@*;
my ($b, $c, $d) = $arr->@[1..3];
my %copy        = $hash->%*;
my %part        = $hash->%{qw/foo/}; # ( foo => 1 )
my ($bar, $baz) = $hash->@{qw/bar baz/};筆者はスライスがpostderefの要だと考えます。ISUCON11では、MYSQL_と書きました。
これは、@{MYSQL_と書きます。@{MYSQL_の部分を括弧を付けずに@{MYSQL_と書くと、MYSQL_が@MYSQL_と扱われないようMYSQL_関数を呼び出すためです。constantプラグマが作る定数が関数であることを知っていなければ、
それと比べ、postderefを使ったスライスは、hostとuserを取り出す意図が簡潔にわかります。
use constant MYSQL_CONFIG => {
  host => '127.0.0.1',
  user => 'isu',
};
# postderefを利用
my ($host, $user) = MYSQL_CONFIG->@{qw/host user/};
# 従来だと
# @{MYSQL_CONFIG()}{qw/host user/} と書く必要がある
# @{MYSQL_CONFIG}{qw/host user/} と書くとエラーになるsignatures ──記号ばかりの引数をやめる
従来のPerlの関数の引数は、@_に保持され、sub add { my ($a, $b) = @_; $a + $b }のように@_を分解して引数を扱います。配列@_の最初の要素を取得するには、my $a = $_[0]やmy $a = shiftとも書けます。これを理解するには、@_の$i番目の要素を$_[$i]と書くことや、shift関数の暗黙の引数に@_が入るといった知識が必要です。これは初学者には優しくなく、
Perl 5.signaturesで、sub add($a, $b) { $a + $b }と簡潔に書けます。従来の方法とsignaturesを使った方法を表1で比較しました。signaturesのほうが簡潔に見えると思います。
@_とsignaturesの比較| 説明 | @_を利用 | signaturesを利用 | 
|---|---|---|
| 2つの引数 | sub f { my ($a, $b) = @_; } | sub f($a,$b) { } | 
| デフォルト値 | sub f { my ($opt) = @_; $opt //= {}; } | sub f($opt={}) { } | 
| メソッド | sub m { my ($self, $key) = @_; } | sub m($self,$key) { } | 
蛇足ですが、sub add($a, $b) { $a + $b }は数値の足し算を期待していても、
sub add($a, $b) {
    Int->check($a) or Carp::croak(Int->get_message($a));
    Int->check($b) or Carp::croak(Int->get_message($b));
    return $a + $b;
}こういった値に制限を加える話は、
<続きの
本誌最新号をチェック!
WEB+DB PRESS Vol.130
2022年8月24日発売
B5判/
定価1,628円
ISBN978-4-297-13000-8
- 特集1
 イミュータブルデータモデルで始める
 実践データモデリング
 業務の複雑さをシンプルに表現!
- 特集2
 いまはじめるFlutter
 iOS/Android両対応アプリを開発してみよう 
- 特集3
 作って学ぶWeb3
 ブロックチェーン、スマートコントラクト、 NFT 


