Perl 5.10からの新機能を一部使ってみる。

  • 初めてのPerlにも書いてあったperl 5.10からの新機能ですが、個人的に5.10以上の環境でも全く使っていなかったので、改めて(今さら)整理してみました。

say

  • javaでいうprintfで、末尾に自動的に改行を付加してくれます。
use 5.010;
say 'Say Hello';
say 'Say Hello';
    • 結果 
Say Hello
Say Hello

defined-or演算子(//)

  • //の左の値がundefだった場合に右の値を返します。||でもよさそうですが、||だと左の値が0や''の時でも右の値を返します。undefの場合だけ右の値を返したい時に使用します。デフォルトの値を設定したい場合なんかに使えます。
use 5.010;
my $var1 = $arg // 'Default Value';
say "var1 is [$var1]";

$arg = ''; 
my $var2 = $arg // 'Default Value';
say "var2 is [$var2]";

$arg = 'Input Value';
my $var3 = $arg // 'Default Value';
say "var3 is [$var3]";
  • var2の値は[Default Value]ではなく、''が設定されています。
var1 is [Default Value]
var2 is []
var3 is [Input Value]

given-when

  • これはswitch文のようなものです。
  • 通常。(whenの条件に一致するとbreakされ、以後の条件一致は行われません。)
use 5.010;
given ('switch') {
    when ( /switch/ )   { say 'match switch' }
    when ( /swit/ )     { say 'match swit' }
    when ( /s/ )        { say 'match s' }
    default             { say 'default' }
}
  • 結果
match switch
  • continueを使用。(マッチした後も処理を続けたい場合に使用します。)
use 5.010;
given ('switch') {
    when ( /switch/ )   { say 'match2 switch'; continue }
    when ( /swit/ )     { say 'match2 swit'; continue }
    when ( /s/ )        { say 'match2 s' }
    default             { say 'default2' }
}
  • 最後のwhenにもcontinueを入れるとdefaultが必ず実行されてしまいます。
match2 switch
match2 swit
match2 s
  • ループの中で使用したい場合
for (qw/foo bar zzz/) {
    when ( /foo/ )   { say 'match foo' }
    when ( /bar/ )   { say 'match bar' }
    default          { say 'default' }
}
  • 結果
match foo
match bar
default

state

  • 関数の中に値を保持出来るレキシカル変数を定義出来るものです。
  • これまでの方法で上記を実現するには(配列を関数内で保持)
sub closure_push {
    my @save_list;
    # 「@save_listに値を追加し、追加した配列を返す関数」を返す
    return sub {
        my @push_list = @_; 
        push @save_list, @push_list;
        return @save_list;
    };  
}

# 関数のリファレンスを受け取る
my $push_array = clojure_push();

my @list1 = $push_array->(1,2,3,4,5);
say "@list1";
my @list2 = $push_array->(6,7,8,9,10);
say "@list2";
  • 結果
1 2 3 4 5
1 2 3 4 5 6 7 8 9 10
  • state関数を使った場合
use 5.010;
sub push_array {
    my @push_list = @_;
    state @save_list;
    #state @save_list = qw/a b c d/;  <= ERROR!
    push @save_list, @push_list;
    return @save_list;
}

my @list1 = push_array(1,2,3,4,5);
say "@list1";
my @list2 = push_array(6,7,8,9,10);
say "@list2";
  • 結果
1 2 3 4 5
1 2 3 4 5 6 7 8 9 10
  • state関数の宣言時にリストコンテキストで初期化することは出来ません。(コメントアウトしている部分)

スマートマッチ

  • これは2つの値をいいように解釈して比較してくれるものです。(a ~~ b)
  • 比較は下記のように実施してくれます。ちゃんと理解して使わないとバグのもとになりそうな気も。。ですが、配列やハッシュの比較には便利ですね。
コード 比較方法
%a ~~ %b ハッシュのキーが全て等しい
%a ~~ @b 少なくとも%aのキーの1つが@bに含まれている
%a ~~ /Fred/ 少なくとも%aのキーの1つがパターンにマッチする
%a ~~ 'Fred" ハッシュのキーが存在する。(この場合は$a{Fred})
@a ~~ @b 配列の内容が等しい
@a ~~ /Fred/ 少なくとも1つの要素がパターンにマッチする
@a ~~ 123 少なくとも1つの要素が数値の123(この場合)である
$name ~~ undef $nameが未定義(undef)である
$name ~~ /Fred/ パターンマッチ
123 ~~ '123.0' 「数値風」文字列に、数値として等しい
'Fred' ~~ 'Fred' 文字列として等しい
123 ~~ 456 数値として等しい

(初めてのPerl 第5版 より引用)

  • 使ってみる
use 5.010;
my @same_list1 = qw/aaa bbb ccc/;
my @same_list2 = qw/aaa bbb ccc/;
if (@same_list1 ~~ @same_list2) {
    say 'Smart Match List!';
}

my %hash = ( 
    key => 'value',
    key2 => 'value2',
);
if (%hash ~~ /key2/) {
    say 'Smart Match Hash!';
}

# 数値と文字列は?
my @num_list = (1, 2, 3); 
my @string_list = ('1', '2', '3');
if (@num_list ~~ @string_list) {
    say 'Smart Match num list ~~ string list';
} else {
    say 'Not Smart Match num list ~~ string list';
}
  • 結果
Smart Match List!
Smart Match Hash!
Smart Match num list ~~ string list

named capture

  • 正規表現のマッチ変数($1,$2...)ではわかりにくいので名前付けることが出来ます。わかりやすくなっていいですね。
(?<LABEL>PATTERN) 
use 5.010;
my $str = '<img src="foo/bar.gif" alt="this is bar" />';
if ( $str =~ /src="(?<src_value>[^"]*)".*alt="(?<alt_value>[^"]*)"/) {
    say "src is '$+{src_value}', alt is '$+{alt_value}'.";
}
  • 結果
src is 'foo/bar.gif', alt is 'this is bar'.

スタックされたファイルテスト演算子

  • -r や -w などを積み重ねるように書けるようになりました。下記のように。
  • 書き込み権限と実行権限があるかどうか調べる
use 5.010;
use FindBin;
# 今まで => if ( -x $FindBin::Script and -w $FindBin::Script) {
if ( -w -x $FindBin::Script) {
    say 'Writable and Executable';
}
Writable and Executable


これからは5.010以上の環境であれば使っていこうと思います。