LoginSignup
31

More than 1 year has passed since last update.

Perl の文法上の新機能が使える feature プラグマ詳解

Last updated at Posted at 2018-07-17

(2022年リリースの Perl 5.36 まで対応しています)

Perl5 は 2010年リリースの 5.12 以降、ほぼ毎年ペースでメジャーバージョンアップしています1。つまり 5.x の偶数 x が +2 されています。

Perl は後方互換性をとても重視した言語で、メジャーバージョンアップで過去の Perl プログラムが動かなくなることはほぼありません。また、バージョンアップによって変わる部分の多くが、速度的な改善であったり、限定的な状況におけるセキュリティ改善だったりといった、インターフェースにかかわらない部分です。

とはいえ、Perl も後方互換性を崩さないよう配慮した上で、実験的な機能として新しい組み込み関数や文法を追加することがあります。 そのときに活躍するのが feature プラグマ です。この記事ではそれら実験的な機能の登場バージョンをまとめました。

  • 文字コード関連のいくつか (unicode_eval, fc など)のうまい解説ができていないので、現状解説を空欄にしてあります。後日追記したところで、ストックしている方に通知を送ります。

特に2022年にリリースされた Perl 5.36 は feature も含めた多数の改良が実施されています。下記のブログが詳しいです。

新たな文法上の機能はどう使う?

前述の通り feature プラグマを使うことで新しい組み込み関数や文法を追加することができます。

feature プラグマは、Perl が新たに提供することになった構文上の機能を、既存の Perl プログラムを壊すことなく選択的に導入するためのものです。

詳しい使い方は perldoc feature日本語版最新英語版) を参照下さい。

実際の使い方を見ていきましょう。

use feature qw(機能名)

使いたい機能を use feature の右側にリストとして列挙します2

use feature qw(使いたい1 使いたい2 ...);

例えば feature が提供する say 関数を使いたい場合は以下のように指定します。

use feateure qw(say); # Perl 5.10 で追加された say を使いたい

2個以上書くこともできます。

use feature qw(state postderef); # Perl 5.10 で追加された state、Perl 5.20 で追加された postderef を使いたい

上記、単語リストの略記 qw を使っていますが、 use feature "state", "postderef"; でも大丈夫です。好みやチームルールに合わせて問題ないです。

なお、feature で提供された機能のうち、現在使用している Perl のバージョンで(安定性が担保されず)実験的とされる機能では、 use feature qw(機能名); をしても実験的である旨警告が出力されます。理解した上で警告を抑止したい場合は、 no warnings qw(experimental::機能名); も合わせて記載します。

# try は Perl 5.36 ではまだ実験的なので警告が出るが、理解しているので警告を抑止する
use feature qw(try);
no warnings qw(exprimental::try);

use feature ":5.マイナーバージョン" または use v5.マイナーバージョン

「Perl 5.20 以上を決め打ちにしているので、そこで安定とされた feature 全部使いたいな」という場合、 use feature ":5.20"; といった記載ができます。

use feature "5:20";
# use feature qw(bareword_filehandles current_sub evalbytes fc indirect multidimensional say state switch unicode_eval unicode_strings) と同じ

この記載で使える機能と、実際にそのインタプリタバージョンで使える feature の機能は違っていて、 use feature ":5.20"; と書いても Perl 5.20 で初登場してまだ実験的とされる postderef などが使えるようにはなりません。また、実験的であると警告が出るものの use feature "5.マイナーバージョン"; で(使ったら警告が出るけれど)使えるようになるものもあります。

なお、use feature ":5.マイナーバージョン"; で何が使えるようになるかは perldoc feature で書かれています。また、この記事中でも下記に表としてまとめています。

これと同等の記述ですが、このバージョン以下の perl での動作を禁止したいという場合

use v5.20;

と書くこともできます。これも use feature ":5.20"; 相当の書き方ですが、使用している perl インタプリタのバージョンがこの指定バージョン以上であるという明示的な指示にもなります3

「原則的に」の例に漏れるもの (array_base) は、下記の解説で触れています。

なお、 use feature qw(機能名); とともに、feature は use したスコープに限定されます。

# 上で use v5.10 や use feature 等をしていない
{
    use feature qw(say);
    say "Hello."; # OK
}
say "Good Morning!"; # NG

ワンライナー -E

ワンライナーにおいて -e ではなく -E を使うことで use feature と同等のことが起こります。下記 Perl 5.28.0 にて、 -e-E の違いを B::Deparse で見ています。

$ perl -MO=Deparse -e 'print "Hello\n"'
print "Hello\n";
-e syntax OK
$ perl -MO=Deparse -E 'print "Hello\n"'
use feature 'current_sub', 'bitwise', 'evalbytes', 'fc', 'postderef_qq', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval';
print "Hello\n";
-e syntax OK

feature による Perl 5.10 以降の新機能

機能ごとに、機能の簡単な解説と、どのバージョンの perl インタプリタから使えるかを表にまとめました。

名前 どういう機能か いつから 備考
say 末尾改行付きprint 5.10
state 二度以降初期化されないmy 5.10
switch given when による switch 文 5.10 非推奨
unicode_string 5.12 5.14で大部分実装、5.16で一部拡張
unicode_eval 5.16
evalbytes 5.16
current_sub 自分自身を __SUB__ トークンで参照できる 5.16
array_base 配列の添字を0始まり以外にする 5.16 非推奨use v5.16; をすると use ではなく no される
fc 5.16
lexical_subs レキシカルサブルーチン 5.18 5.26から警告無し
postderef 後置デリファレンス 5.20 5.24から警告無し
postderef_qq 後置デリファレンスをダブルクォート内で展開 5.20 postderef 同様
signatures サブルーチンシグネチャ(仮引数) 5.20
refaliasing リファレンスによる別名変数定義 5.22
bitwise ビット演算子を数値専用と文字列専用に分ける 5.22
declared_refs リファレンスの直接宣言 5.26
isa 二項演算子 isa の導入 5.32
indirect 間接オブジェクト記法の許可または禁止 5.32
multidimensional 多次元配列のエミューレーションの許可または禁止 5.34
bareword_filehandles 裸のワードのファイルハンドルの許可または禁止 5.34
try try/catch構文の導入 5.34
defer defer構文の導入 5.36
extra_paired_delimiters qrなどで対として使える括弧文字を多数導入 5.36

試験的に使えるかどうか、使えても警告が出るかどうかで Perl バージョンごとに見た表も作成してみました。2021年10月追加現在で :warning: 部分のリサーチが少し甘いので、実際には no warnings qw(exprimental::機能名) せずとも警告なしかもしれません。もし間違っていたらお知らせ下さい。

5.x 10 12 14 16 18 20 22 24 26 28 30 32 34 36
say :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
state :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
switch :ok: :ok: :ok: :ok: :warning: :warning: :warning: :warning: :warning: :warning: :warning: :warning: :warning: :warning:
unicode_string :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
unicode_eval :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
evalbytes :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
current_sub :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
array_base :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
fc :warning: :warning: :warning: :warning: :warning: :ok: :ok: :ok: :ok: :ok: :ok:
lexical_subs :warning: :warning: :warning: :warning: :ok: :ok: :ok: :ok: :ok: :ok:
postderef :warning: :warning: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
postderef_qq :warning: :warning: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
signatures :warning: :warning: :warning: :warning: :warning: :warning: :warning: :warning: :ok:
refaliasing :warning: :warning: :warning: :warning: :warning: :warning: :warning: :warning:
bitwise :warning: :warning: :warning: :ok: :ok: :ok: :ok: :ok:
declared_refs :warning: :warning: :warning: :warning: :warning: :warning:
isa :warning: :warning: :ok:
indirect :ok: :ok: :ok:
multidimensional :ok: :ok:
bareword_filehandles :ok: :ok:
try :warning: :warning:
defer :warning:
extra_paired_delimiters :warning:

use feature ":5.マイナーバージョン" で何が使えるかも表にしてみました。

bundle default 10 12 14 16 18 20 22 24 26 28 30 32 34 36
bareword_filehandles :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
bitwise :ok: :ok: :ok: :ok: :ok:
current_sub :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
evalbytes :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
fc :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
indirect :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
isa :ok:
multidimensional :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
postderef_qq :ok: :ok: :ok: :ok: :ok: :ok: :ok:
say :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
signatures :ok:
state :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
switch :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
unicode_eval :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:
unicode_strings :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok: :ok:

bundle 行が 14 と書いてある列は use feature ":5.14"; または use v5.14; としたときの feature の機能が有効になる (:ok:) かどうかです。

bundle 行が default の列で :ok: となっているものは、 use feature がそもそもなくとも最初から有効になっているものです。

この default の :ok: ですが、歴史的に大昔から存在する文法であるものの好ましくないとされるものです。そのまま無くしてしまうと互換性に影響を与えてしまうものの、自分またはチームが書くプログラムでは好ましくない文法を使わないようにしたい場合、 no feature qw(bareword_filehandles indirect); などとすることによって機能を無効にすることを意図しています。

default :ok: で大昔から使うことができた indirect や multidimensional も、 Perl 5.36 (以降)の perl インタプリタに対して use v5.36 または use feature ":5.36" とすることでいよいよ使えなくなることが見て取れます。

機能ごとの詳細

say - 末尾改行付き print

解説の通りです。

いくつかの言語では println 、PHP でいう echo 、Ruby でいう puts のようなものです。

間接オブジェクトスロットにファイルハンドルが置かれることを想定しなければ(つまり say {$fh} "Hello" という形での呼び出しではないということ)、誰もが簡単に書くことができます。

sub say {
    print @_, "\n";
}

state - 二度目以降初期化されない my

state は、カウンターサブルーチンなど、状態を持ったサブルーチンを定義する時に使うものです。state は特殊な my として扱われ、定義できるものは my と同じレキシカル変数ですが、同じ行で二度以降初期化されないという特性を持っています。

例えば、呼び出すごとに返却値の数値が増えるカウンターサブルーチンを定義してみましょう。

### これは間違い
sub count {
    my $count = 0;
    return ++$count;
}

print count(), "\n"; # 1
print count(), "\n"; # 1
print count(), "\n"; # 1

この場合、 my $count = 0; の行は count() を呼び出すたびに初期化処理が入ります。

今までの構文でカウンターを実現するには、 my $count は外に出しつつ定義します。

my $count = 0;
sub count {
    return ++$count;
}

print count(), "\n"; # 1
print count(), "\n"; # 2
print count(), "\n"; # 3

count サブルーチン以外の外部( my $count = 0 の下にある行)から $count を操作できることを気にするのであれば、全体を無名ブロックで囲うと良いです。

{
my $count = 0;
sub count {
    return ++$count;
}
}

print count(), "\n"; # 1
print count(), "\n"; # 2
print count(), "\n"; # 3

この無名ブロックで my 宣言と sub サブルーチン宣言を囲う書き方は古くからの Perl のイディオムともいえるものですが、初学者が混乱する構文であるかもしれません。

これを同様のことを簡素に書けるようにしたのが state です。

use feature qw(state);
sub count {
    state $count = 0;
    return ++$count;
}

print count(), "\n"; # 1
print count(), "\n"; # 2
print count(), "\n"; # 3

もちろん、state 宣言の行と同じ文で行われた代入が一度だけということなので、別の行で $count に代入操作を入れると count サブルーチンの中の $count の値は変わってしまうことに注意して下さい。

use feature qw(state);
sub count {
    state $count; # state 宣言の行は全部通して一度だけ評価される
    $count = 0;   # ここは通常の代入の行なので何度も評価される
    return ++$count;
}

print count(), "\n"; # 1
print count(), "\n"; # 1
print count(), "\n"; # 1

switch - given-when とスマートマッチ

switch は、多くの言語でいう switch-case 構造を再現するものです。

use feature qw(switch say);
my $arg = shift;
given ($arg) {
   when (1)         { say "one"; }
   which (/^Hello/) { say "Hello!"; }
   default          { say "How are you?"; }
}

多くの言語にある switch-case 文と似ていますが、 swtich にあたる given で指定された $arg が、各 when で指定された値との比較がスマートマッチ ~~ という二項演算子によって行われます。

use feature qw(switch say);
my $arg = shift;
if ($arg ~~ 1) {
    say "one";
} elsif ( $arg ~~ /^Hello/) {
    say "Hello!";
} else {
    say "How are you?";
}

しかし、Perl 5.10.1 で導入されたスマートマッチは、当時から仕様が複雑だったりといった問題が指摘される機能で、Perl 5.18 でいったん非推奨となりました

そのため、スマートマッチとともに導入された given-when も、スマートマッチと共に Perl5.18 より非推奨となっています。

switch を使用する場合も、スマートマッチ関連の警告が出るので

no warnings "experimental::smartmatch";

と書いて警告を抑止する必要があるかもしれません。スマートマッチについての解説をよく読んで、使用する場合も様々なリスクを理解する必要がありそうです。

unicode_string

(stub)

unicode_eval と evalbytes

(stub)

current_sub - 自分自身を __SUB__ トークンで参照できる

Perl でもサブルーチンを再帰で書くことがあります。

#!/usr/bin/perl

use strict;
use warnings;
use feature qw(say);

say fib(5);

sub fib {
    my $n = shift;
    if ( !defined $n || !length $n || $n =~ /\D/ ) {
        die "fib: 1st argument is required as digit.\n";
    } elsif ( $n == 0 || $n == 1 ) {
        return 1;
    } else {
        return fib($n-1) + fib($n-2);
    }
}

上記は平凡なフィボナッチ数列の第$n項を求めるサブルーチン(関数)です。

例えば、初項と第二項を事前に選んだフィボナッチ数列を求めるサブルーチンを返すサブルーチンを書くとしたらどのように書くと良いでしょうか。

#!/usr/bin/perl

use v5.10;
use strict;
use warnings;

my $fib = generate_fibonacci_sequence(3, 7); # 3 7 10 17 27 44 ...
say $fib->(5); # 44

sub generate_fibonacci_sequence {
    my ($first, $second) = @_;
    for ($first, $second) { # defined 等、暗黙の $_ を参照していることに注意
        if ( !defined || !length || !m{^-?(?:\d+)(?:\.\d+)?$} ) {
            die "generate_fibonacci_sequence: 1st and 2nd arugment is quired as integer.\n";
        }
    }
    my $fib; $fib = sub {
        my $n = shift;
        if ( !defined $n || !length $n || $n =~ /\D/ ) {
            die "fib: 1st argument is required as digit.\n";
        } elsif ( $n == 0 ) {
            return $first;
        } elsif ( $n == 1 ){
            return $second;
        } else {
            return $fib->($n-1) + $fib->($n-2);
        }
    };
    return $fib;
}

クロージャと言われるような書き方で対応しました。my $fib; $fib = ... という風に宣言(my) と代入(=) を別の文に分けているのは、代入が右結合なので、左辺の宣言評価の前に右辺のサブルーチンリファレンスが評価されることになり、その中にある $fib はまだ宣言されていないことで use strict によって未宣言エラーとなるためです。

最初の簡素な fib() サブルーチンの構造を包含するように generate_fibonacci_sequence() は複雑なサブルーチンとなりましたが、再帰呼び出しを使っている発想は同様です。

「無名関数」とも呼ばれるサブルーチンリファレンスで再帰呼び出しを行う必要がありましたが、今回は無名といいつつ、すぐに $fib という「名前」を付けることができたので、これを再帰呼び出しのネタとして使うことができました。

サブルーチンリファレンスが必要とされる場面で、そのサブルーチンリファレンス自体が再帰呼び出しロジックを必要とする場合には、上記のように即座にスカラー変数に代入して「名前」を付けることで対応することができます。

しかし、サブルーチンを返却するサブルーチンを返却するサブルーチン…といった多階層構造になったり、ある程度大掛かりなオブジェクトのメソッドにコールバックを渡したり…といった場面において、「名前」を付けることが手間になる場面もあります。

そのような場合、サブルーチンが自分自身を指す名前が既に無くとも、__SUB__ と書くことで自分自身のリファレンスを取得できる機能が current_sub です。

#!/usr/bin/perl

# どちらかの use を書くと current_sub が有効になる
use v5.16;
use feature qw(say current_sub);

use strict;
use warnings;

my $fib = generate_fibonacci_sequence(3, 7); # 3 7 10 17 27 44 ...
say $fib->(5); # 44

sub generate_fibonacci_sequence {
    my ($first, $second) = @_;
    for ($first, $second) {
        if ( !defined || !length || !m{^-?(?:\d+)(?:\.\d+)?$} ) {
            die "generate_fibonacci_sequence: 1st and 2nd arugment is quired as integer.\n";
        }
    }
    return sub {
        my $n = shift;
        if ( !defined $n || !length $n || $n =~ /\D/ ) {
            die "fib: 1st argument is required as digit.\n";
        } elsif ( $n == 0 ) {
            return $first;
        } elsif ( $n == 1 ){
            return $second;
        } else {
            return __SUB__->($n-1) + __SUB__->($n-2);
        }
    };
}

__SUB__ が使えることになったため、$fib の宣言と代入が省かれてシンプルになりました。

my $fib; $fib = sub { ... }; という宣言と代入を分ける書き方は AnyEvent 関連モジュール等、コールバックを多用する構造で多用する書き方ではありますが、Perl の初学者や中級者には初見殺し(宣言と代入の概念をわかりやすく書いた文献がほぼないからとも言えますが)でもあるので、__SUB__ による書き方は簡潔さだけでなく初学者にも優しいと感じます。

Perl には古くからサブルーチン自身もしくは呼び出し元パッケージを調べる caller 組み込み関数がありますが、これとは違う用途で使われるものです。

array_base - 配列の添字を0始まり以外にする

多くのプログラム言語の例にもれず、Perl も配列の添字の始まりは (1ではなく) 0 です。

ただ、Perl が登場する前に存在していた古い AWK や Fortran といったプログラム言語では、配列の添字の始まりは 1 だったとのことで、その振る舞いを Perl で再現するため、配列の添字の始まりを変えるための特殊なグローバル変数 $[ というものがありました。

# Perl 5 以降で
# 添字を1始まりにする
$[ = 1;

この $[ の詳細は perlvar の当該項目を参照下さい。

そもそも、Perl の添字の始まりを変更することは、他の外部ファイルなどへの副作用が大きいと恐れるのは自然なことで、この添字を変更する $[ の使用は Perl 5.12 で非推奨となっています。ともかく私も使ったことがなく(添字が 0 始まりで困ったことがない)、起こりうる副作用や混乱(プログラミングの共同作業者に誤解を与えるところから)を考えると怖くて使えません。

ここからは Perl 5.16 以降の話です。

Perl 5.16 以降では $[ 変数の動作は arybase プラグマが制御しています。$[ に代入しようとすると、裏側で自動的に use arybase; が行われます。

#!/usr/bin/env perl

use strict;
use warnings;

use feature qw(say);

# 以下の代入が発生する直前に use arybase; が暗黙で呼ばれる
$[ = 1;

my @array = (qw(first second third fourth));

say "array[1] = $array[1]"; # first

しかし、上記のプログラムを実行すると警告が出ます。

Use of assignment to $[ is deprecated at feature_example_array_base.pl line 8.
array[1] = first

arybase プラグマが対応することになったとはいえ、要するに非推奨というわけですね。

この $[ の機能を完全に封じたいと考えるのは自然で、これを行うのが Perl 5.16 以降で使えることになった no feature qw(array_base); プラグマです。肯定的なプラグマ呼び出し use ではなく、否定的なプラグマ呼び出し no なのがポイント。

#!/usr/bin/env perl

use strict;
use warnings;

use feature qw(say);
no  feature qw(array_base);

$[ = 1;

my @array = (qw(first second third fourth));

say "array[1] = $array[1]";

こう書くと、$[ = 1; の代入(具体的には 0 以外の代入)が発生した時点で実行時エラーが発生します。

Assigning non-zero to $[ is no longer possible at feature_example_array_base.pl line 9.

say などは、その登場バージョン以上のバージョン番号で use v5.10; とすると use feature qw(say); などとされたと同じとみなされて使えるようになります。しかし array_base については特殊で、それが登場した perl-5.16 以降で実は use v5.16; とすると array_base に関しては use feature qw(array_base); ではなく no feature qw(array_base); されたとみなされることになります。

ただ、use v5.16; と書きたいけれど、深遠な事情で $[ = 1; はしたいという場合に、use feature qw(array_base);$[ = 1; を書くことを許可するようにできるというものです。

#!/usr/bin/env perl

use strict;
use warnings;

use v5.16;
use feature qw(say);
use feature qw(array_base);

$[ = 1;

my @array = (qw(first second third fourth));

say "array[1] = $array[1]";

use feature は複数の機能をまとめて書いてもいいですし、上記のように行を分けて書いても良いです。

use v5.16; 時点で $[ = 1; は実行時エラーとなりますが、use feature qw(array_base); をすることで実行時エラーを回避して、arybase プラグマを利用して当該機能を Perl 5.16 以降でも使用することができるという流れです。

Use of assignment to $[ is deprecated at feature_example_array_base.pl line 10.
array[1] = first

とはいえ、use feature qw(array_base); したところで非推奨なことに変わりません。

実際に内部的にどのようなコードが実行されているのかは、B::Deparse を使うことでわかります。

$ perl -e 'use B::Deparse; use v5.16; use feature "say"; say B::Deparse->new("-p", "-sC")->coderef2text(sub { 1; })';
{
    use strict;
    use feature 'current_sub', 'evalbytes', 'fc', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval';
    no feature 'array_base';
    1;
}

use v5.16; の後に何らかの use feature があると、実際にどの feature が有効無効になっているのか、B::Deparse で見ることができます。

fc - 文字のfolding

(stub)

lexical_subs レキシカルサブルーチン

レキシカルサブルーチンとは my sub foo といった、書かれたスコープ内でのみ有効な新しいサブルーチンの書き方を導入するものです。

lexical_subs は、Perl 5.18 から Perl 5.24 までは

The lexical_subs feature is experimental

といった警告が出てきてしまいます。理解して使っているのでこれを抑止したいという場合には

no warinigns "experimental::lexical_subs";

と書きます。

Perl 5.26 から上記警告が出なくなりました。

#!/usr/bin/env perl
use strict;
use warnings;

use feature qw(lexical_subs say);

my $n = 1;

if ( $n == 1 ) {
    my sub info {
        my @args = @_;
        say "[info] @args";
    }
    info("foo");
}

info("bar");

これを実行すると

[info] foo
Undefined subroutine &main::info called at tmp/feature_example_lexsub.pl line 17.

といった表示となります(上述の通り 5.24 までの Perl では experimental 旨警告が出る場合もあります)。if のスコープの中でのみ有効な info は実行されるものの、そのスコープの外で info を呼び出そうとしたらエラーとなるサンプルです。

レキシカルサブルーチン my sub foo は、今まではサブルーチンリファレンスで代用できていたものです。

#!/usr/bin/env perl
use strict;
use warnings;

use feature qw(say);

my $n = 1;

if ( $n == 1 ) {
    my $info = sub {
        my @args = @_;
        say "[info] @args";
    }
    $info->("foo");
}

$info->("bar"); # $info が定義されていないことは明白なので、ここでエラーとなる

私も、サブルーチンリファレンスを使わずあえてレキシカルサブルーチンを使うとよい上手い使いどころがわからず、様子見状態です。

変数の my 宣言以外にも特殊な my である state やグローバル変数の宣言を行う our があるように、use feature qw(lexical_subs); では my sub foo 以外にも、それに対応した state sub fooour sub foo といった記法を使うことができます。

state sub foo については、変数の state と同様にそのスコープ内で一度しか初期化されないサブルーチンを定義します。うまい例が思いつかなかったので例は省略しますが、 forwhile の中で繰り返されるループ内に my sub foo があると、都度サブルーチン初期化のコストがかかるわけで、 state sub foo が求められる場面があるかもしれません(その前に my sub foo 自体に意義を見出さなければなりませんが)。

our sub foosub foo と全く同じ意味だと思います。 our が意味するところはグローバル変数 == パッケージと紐付いた変数であるわけです。この辺りはPerl のグローバル変数やレキシカル変数について - Qiitaを参照下さい。

サブルーチン宣言のブロック中にサブルーチン宣言を書いた場合、コンパイルは通りますが、内側のサブルーチンは外側のサブルーチンから出て並列で書かれたかのようにコンパイルされます。この挙動をサブルーチンの巻き上げと呼ぶ人もいます。この際、外側のサブルーチンのブロックが持つレキシカル変数を内側のサブルーチンが共有していると、意味が曖昧になって警告が出ます。

#!/usr/bin/env perl

use strict;
use warnings;

use feature qw(say lexical_subs);

sub handler {
    my $handler_call_count = 1;
    # このように sub calc を書いても、コンパイル時には
    # sub handler と sub calc を入れ子ではなく並列に書いたように解釈される
    sub calc {
        my $n = shift;
        return $n * $handler_call_count++;
    }
    say calc(20);
    say calc(-26);
}

handler();

このスクリプトを実行すると

Variable "$handler_call_count" will not stay shared at feature_example_globalsub.pl line 14.
20
-52

と警告が表示されます。

上記例では sub calc でも our sub calc でも同様の警告が表示されますが、 my sub calcstate sub calc であれば変数が曖昧に共有されることもなく、警告は出ません。

handlercalc は入れ子のように書かれていながら、実際は並んで書かれたかのように解釈されるものの、そのように解釈すると、 handler の外に出てしまった calchandler の関数スコープの中にある $handler_call_count を参照しようとする部分が曖昧となってしまうためです。

もちろん、返り値としてサブルーチンリファレンス(関数参照)を返せば、 handler 内の $handler_call_counthandler が呼び出すたびに別途生成され、モダンなプログラミング言語で活用されるクロージャ的動作となります。

#!/usr/bin/env perl

use strict;
use warnings;

use feature qw(say lexical_subs);

# handler はカウンターを返す
sub handler {
    my $handler_call_count = 1;
    # このように sub calc を書いても、コンパイル時には
    # sub handler と sub calc を並べて書いたように解釈される
    return sub {
        return $handler_call_count++;
    }
}

my $counter_a = handler();
my $counter_b = handler();


say $counter_a->(); # 1
say $counter_b->(); # 1
say $counter_a->(); # 2
say $counter_a->(); # 3
say $counter_b->(); # 2

postderef と postderef_qq - 後置デリファレンス

Perl では配列リファレンスやハッシュリファレンスのデリファレンスにアロー記法 -> を使います

my $first_line = $response->{body_lines}->[0];

上記 $response のデータ構造だと、レスポンスボディの最初の行」といった書き方。 $response がハッシュリファレンスで、そのキー(key) body_lines に対応する値(value)が配列リファレンスで、行ごとに(配列リファレンスがさす実態としての)配列の要素となっていると読めます。

たとえば、行番号として添字を指定するのではなく繰り返し指定したい場合には、 for で繰り返し処理を書きます。

for my $line (@{$response->{body_lines}}) {
    ...
}

とはいえ、プログラムを書く思考の流れ的にはまず $response->{body_lines} と書いた後で、「そうそうこれは配列リファレンスで、その要素全体をリストでほしいんだった(リストコンテキストに配列を置くとリストが得られる)」と思い立って、カーソルを左側に戻して @{ ... } と書くことがたびたびあります。かくいう私もそういう行動をしています。

であれば、思考の流れをそのまま左から右へ書いていけると嬉しいという考えのもと生み出されたのが、後置デリファレンス (postfix dereference) です。

後置デリファレンスは use feature qw(postderef); とすることで使うことができ、

@{$response->{body_lines}}

use feature qw(postderef); # 離れた場所で定義されていても OK

$response->{body_lines}->@*

と書くことができるようになります。

use feature qw(postderef);

for my $line ($response->{body_lines}->@*) {
    say $line;
}

配列リファレンス以外にも記法があります。下記例は perlref より。

  $sref->$*;  # same as  ${ $sref }
  $aref->@*;  # same as  @{ $aref }
  $aref->$#*; # same as $#{ $aref }
  $href->%*;  # same as  %{ $href }
  $cref->&*;  # same as  &{ $cref }
  $gref->**;  # same as  *{ $gref }

昔からの Perl プログラマの間には「後置デリファレンスはうけつない」という人が多いように感じますが、たぶん ->@** が何を意味しているのかわからないという部分が大きいのかなと思います。

この * 部分には、いわゆるスライス構文を置くことができます。

use feature qw(postderef);

# 5行目から10行目までが欲しい
my @part_lines = $response->{body_lines}->@[4..9];

postderef に似た postderef_qq は、後置リファレンスをダブルクォート(もしくはそれと同等な qq{} など)の中で展開するものです。

print "array is @array\n"; # @array が空白区切り(実際は変数 $" 区切り)で内挿される

use feature qw(postderef postderef_qq);
print "array is $response->{body_lines}->@*";

後置デリファレンスがない時代に "$foo->{bar}->@*" 等と書かれた文字列を構文エラーにしてしまわないため、postderef と postderef_qq が分かれています。こんな部分まで後方互換性を気にしてくれるのは、Perlならではと言えましょう。

後置デリファレンスの考え方については、brian d foy 氏による以下の記事がわかりやすいです。

また、詳細は最新の perlref を参照下さい。

signatures - サブルーチンシグネチャ(仮引数)

Perl には多くのプログラミング言語にある関数の仮引数がないということがよく槍玉に挙げられます。

sub add {
    my ($left, $right) = @_;
    # もしくは
    #     my $left = shift;
    #     my $right = shift;
    retrun $left + $right;
}

例えば JavaScript であれば

function add(left, right) {
    return left + right;
}

と仮引数を使って書けるわかりやすさを Perl に導入したものがサブルーチンシグネチャです。

use feature qw(signatures say);

sub add ($left, $right) {
  return $left + $right;
}

say add(5, 7);

ただし Perl 5.26 現在、そのままでは

The signatures feature is experimental

といった警告が出てきてしまいます。理解して使っているのでこれを抑止したいという場合には

no warnings "experimental::signatures";

と書きます。

Perl 5.36 から上記警告が出なくなりました。

Perl にはプロトタイプ宣言というサブルーチンシグネチャに似た構文が大昔からあり、サブルーチンシグネチャを使う場合にはプロトタイプ宣言と混乱しない使い方が求められます(主にプロジェクトメンバーに対して)。

プロトタイプ宣言の良し悪しはこの記事では割愛しますが、サブルーチンシグネチャとプロトタイプ宣言を同時使用したい場合は、新たに導入された :prototype 属性 (attribute) を使用することができます。

sub add ($$) { # 必ず2引数を強制するプロトタイプ宣言
    my $left  = shift;
    my $right = shift;
    return $left + $right;
}

というプロトタイプ宣言をサブルーチンシグネチャと混用したければ

use feature qw(signatures say);
no warnings "experimental::signatures";

# 5.20 および 5.28 以降
sub add :prototype($$) ($left, $right) {
    return $left + $right;
}

# 5.22、5.24、5.26
sub add ($left, $right) :prototype($$) {
    return $left + $right;
}

と書くと良いでしょう。

プロトタイプ宣言とシグネチャの順序が、Perlのバージョンに応じて変わっています(Perl5.28 での変更は :lvalue の場合の問題回避とのこと)。それほど多くはないと思いますが、両者を混ぜて使う場合は注意しましょう。

詳細は perlsub を参照下さい。

refaliasing - リファレンスによる別名変数定義

refaliasing は別名変数を定義する方法を提供するものです。

use feature qw(refaliasing say);

my ($x, $y);
\$x = \$y; # refaliasing

$y = "hello";
say "x is $x"; # hello
$x = "hi";
say "y is $y"; # hi

リファレンスによる別名を \$x = \$y とした後は、$x$y は全く同じ値を持つことになり、かつ $x の変更が $y にも「同期」され、その逆も同様となります(つまり、 \$x = \$y\$y = \$x は意味的に同じです)。

今までの Perl では、リファレンスを取得する \$x といった構文を代入の左辺に持ってくることはできなかったのですが、それを可能にするのが refaliasing です。

ただし Perl 5.28 現在、そのままでは

Aliasing via reference is experimental

といった警告が出てきてしまいます。理解して使っているのでこれを抑止したいという場合には

no warnings "experimental::refaliasing";

と書きます。

ちなみに、refaliasing はスカラーだけでなく、配列やハッシュでも同様に機能します。

use feature qw(refaliasing);

my $hashref = {
   name => "Bull",
   type => "Dog",
}
my %x;
\%x = $hashref; # refaliasing

refaliasing を使うと、サブルーチン・メソッドの別名定義が明快になります。

Perl5 にてサブルーチン・メソッドの別名定義をする場合、以下のように別名としたい名前のグロブ変数にサブルーチンリファレンスを代入していました。

sub search {
    ...
}
# find は search の別名
*find = \&search;

グロブは Perl4 時代からあるリファレンスの始祖のような存在ですが、それゆえマジカルで古い書き方のような印象を覚えます。もっとも、モダンな Perl5 コーディングにおいて、グロブはこのようないくつかの用例のみがあるイディオムと扱えば良いものの、これも refaliasing でより明快な書き方ができます。

use feature qw(refaliasing);
sub search {
    ...
}
# find は search の別名
\&find = \&search;

スカラー・配列・ハッシュといったデータ型は my $find と宣言していない状況で \$find とリファレンスを書くと strict によって例外となってしまいますが、サブルーチン find (または &find)が定義されていない状態で \&find と書いても問題ありません。

もっとも、my $cb = \&find; の後で $cb->() と呼び出したときに

Undefined subroutine &main::find called

と警告を受けることになるでしょう。

なお、サブルーチンの別名を定義しようとして以下のようにすると表面的には要望を実現できますが、ある種の状況(caller 等で呼び出しを詳細に観察している場合)で混乱することがあるかもしれません。またパフォーマンスは上記による別名作成よりも少し悪いでしょう。

sub find {
    search(@_);
}

なお、下記であれば上記より多少問題が緩和されます。

sub find {
    goto &search;
}

詳しくは gotocaller の解説を参照下さい。

少し話が飛んでしまいましたが、refaliasing の話に戻りましょう。

別名変数とは、もともとは for で指定される受け渡し変数や、 map が伴うブロック中の $_ のことを言いました。

my @numbers = (1...5);

for my $number (@numbers) {
    # 二乗した値を表示したいので $number をまず二乗する
    $number **= 2;
    print "number is $number\n";
}

# なぜか @numbers の配列の内容が書き換わっている
print join ", ", @numbers; # 1, 4, 9, 16, 25

上記では、一時的に for のスコープ中の一回のループでのみ有効な $number を変更しただけなのに、なぜか配列 @numbers 自体が書き換わっています。

実は、上記のように for $number (@numbers) といった配列を丸括弧内に置く書き方をすると、例えば初回ループでの $number$numbers[0] と真に同一の変数となるのです。つまり $number を破壊すると $numbers[0] も破壊することになります。

このような挙動の変数は formap で有名です。

上記を意図通り書くとすれば、以下のように書きます。

my @numbers = (1...5);

for my $number (@numbers) {
    print "number is " . ($number ** 2) . "\n";
}

print join ", ", @numbers; # 1, 2, 3, 4, 5

「二乗 ** ならそんな書き方しないでしょう」という方も、破壊が基本の $string =~ s/// 構文等で上記のような失敗を踏んでしまうことは十分考えられます(そして Perl5.14 以降で使えるようになった非破壊置換 s///r も調べてみて下さい)。

なぜ上記のような動作になっているのか。私は「変数の内容をコピーするコストを抑えるため」と理解しています。

なお、上記の for の別名変数の特性を逆に利用する書き方もあります。

# 配列の中のキーワード全てを最初に小文字に変換しておく
for (@keywrods) {
    $_ = lc $_;
}

# 一時的に長い変数名を反復して書きたくない
for ($very_very_long_name_variable) {
    s/foo/BAR/;
    s/buz/QUUX/;
    $_ = quotemeta($_);
}
# 文全体の文字が少なければ、後置 for で `s///, s///, s/// for $string` と書いてもよい

横道にそれてしまいましたが、別名変数であることはチームメンバーに対して混乱を生む可能性もあるので、for 等の別名変数を意図した使用や refaliasing の使用に際しては、その妥当性を十分検討してチーム全体の合意を取る必要があるでしょう。

bitwise - ビット演算子を数値専用と文字列専用に分ける

Perl にはビットに対する AND OR XOR といった演算を行うビット演算子があります。

  • ビットごとの論理積 &
  • ビットごとの論理和 |
  • ビットごとの排他的論理和 ^
use feature qw(say);

say  105  |  150;  # 255
say "105" | "150"; # 155

ビット演算子は、両辺が数値か文字列かで意味が変わってきます

  • 右辺か左辺どちらかが数値であれば、数値のビット演算
  • 右辺と右辺のどちらも文字列であれば、各桁の文字についてビット演算したもの

もし、外部から受け取ったスカラー変数をビット演算で評価する場合、それが数値なのか、(数値に見せかけた)文字列なのかを厳密に扱いたい場合があります。

今までの Perl でも、明示的な数値化 0+$x や文字列化 "$x" を使うことで回避していました。

use feature qw(say);

sub bit_add {
   my ($left, $right) = @_;
   # 引数は必ず数値
   return 0+$left | 0+$right;
}

# 外部から <STDIN> などで読み込んだデータは数字であっても、
# データとしては文字列になっている
say bit_add("150", "105"); # 255

しかし、 0+$x"$x" は少々トリッキーな書き方ということもあり、これを解決するための bitwise feature が生まれました。

use feature qw(bitwise); をすると

  • ビット演算子 | & ^ は必ず両辺を数値として扱う
  • 新たに |. &. ^. というビット演算子が導入され、それらは必ず両辺を文字列として扱う

という変更が発生します。

use feature qw(say);

sub bit_add {
   my ($left, $right) = @_;
   # 引数は必ず数値
   use feature qw(bitwise); # このスコープでのみ `|` の挙動を変えたい場合はここで宣言するとよい
   return $left | $right;
}

say bit_add("150", "105"); # 255
say "150" | "105";         # 155

ただし Perl 5.26 までは、use feature qw(bitwise); 時にビット演算を行うと

The bitwise feature is experimental

といった警告が出てきてしまいます。理解して使っているのでこれを抑止したいという場合には

no warnings "experimental::bitwise";

と書きます。

Perl 5.28 より、警告が出なくなりました。

declared_refs - リファレンスの直接宣言

declared_refs を使うと、リファレンス自体を my 宣言することができます。

use feature qw(declared_refs);
my \$x; # \$x を宣言する、つまり $x も同時に宣言する

これの何が嬉しいのかというと、refaliasing との相性が良いことでしょう。

refaliasing では、リファレンスの直接定義ができると書きました。

use feature qw(refaliasing say);

my ($x, $y);
\$x = \$y; # refaliasing

$y = "hello";
say "x is $x"; # hello
$x = "hi";
say "y is $y"; # hi

この場合、変数宣言自体を行った後で代入 \$x = \$y を行っていることがわかります。refaliasing を多用する場合、これが手間だという人もいるでしょう。

この手間を省き、変数のリファレンスを直接宣言できるようにするのが declared_refs です。

use feature qw(declared_refs refaliasing say);

my \$x = my \$y; # declared_refs && refaliasing

$y = "hello"; # $x に初めて値を入れる
say "x is $x"; # hello
$x = "hi";
say "y is $y"; # hi

ただし Perl 5.28 現在、そのままでは

Declaring references is experimental

といった警告が出てきてしまいます。理解して使っているのでこれを抑止したいという場合には

no warnings "experimental::declared_refs";

と書きます。

もっとも、 my \$x; は今までできなかったわけですが、\my $x; という書き方はできました(バッククォートの位置に注目)。my は宣言に成功したら式全体は宣言した空の変数そのものとして使うことができるので、以下のような書き方で上記と同様なことができます。


# declared_refs がなくても以下はできる
\my $x = \my $y;

\my $x ができれば declared_refs 要らないのでは?」という疑問も浮かびますが、そもそも \my $x という表記が初見殺しであること、また丸かっこでまとめて my 宣言する場合に \my ($x, $y, $z) の式全体の評価値が (\$x, \$y, \$z) となることが都合悪い場合もあることも declared_refs の後押しになったのではと感じています。詳細は perlref - Declaring a Reference to a Variable を参照下さい。

refaliasing にまつわる警告は refaliasing の節を参照下さい。

isa - 二項演算子 isa の導入

isa を使うと、二項演算子 isa を導入することができます。

use feature qw(isa say);
use HTTP::Tiny;

my $ua = HTTP::Tiny->new();
# $ua->isa("HTTP::Tiny") を $ua isa "HTTP::Tiny" と書ける
if ( $ua isa "HTTP::Tiny" ) {
    say "ua inherits HTTP::Tiny";
} else {
    say "ua does not inherit HTTP::Tiny";
}

もともと Perl には、全てのオブジェクトから呼び出せるメソッド isaUNIVERSAL::isa ) が定義されていて、 $obj->isa($class) でオブジェクト $obj がクラス $class または $class を継承したオブジェクトかを検査することができました。

二項演算子 isa は、この isa メソッドを二項演算子として書けるようにしたものです。

ただし、Perl 5.32 現在、そのままでは

isa is experimental

といった警告が出てきてしまいます。理解して使っているのでこれを抑止したいという場合には

no warnings "experimental::isa";

と書きます。

Perl 5.36 から上記警告が出なくなりました。

なお二項演算子 isa の優先順位はたぶん多くの場合において適切な優先順位となっており、他の二項演算子と併用しても問題ないでしょう。詳細は perlop を参照下さい。

# 優先順位は . → eq → isa → && なので、大体の人が意図した通りの真偽値になる
# つまり if ( ... ) 中の優先順位変更の丸括弧は不要(そのとおりの優先順位)となる
if ( ($obj isa ("MyClass::" . $ns1) ) && ($arg1 eq "+" . $ns1) ) {
    ....
}

indirect - 間接オブジェクト記法の許可または禁止

indirect を使うと、間接オブジェクト記法を許可したり禁止することができます。

間接オブジェクト記法とは、 ClassName->method(@args)method ClassName $obj (@args) と書けるといった Perl5 の大昔からある文法です。これを使うと、Java などに見られるコンストラクタ new の書き方を Perl で my $foo = new ClassName @args と真似ることができることが嬉しいとされています。

しかし、この間接オブジェクト記法は曖昧さを含んでおり、

my $foo = new ClassName @args;

my $foo = ClassName->new(@args);

なのか

my $foo = new(ClassName(@args));

なのか混同する文脈が一部存在することが問題となりました。

上述のような混同する文脈は Perl のバージョンアップとともに改善されたりもしているようですが、それでも曖昧さを含む状況が排除できない場合もあります。

であれば、チームのコーディング規約から一歩踏み込んで文法レベルで間接オブジェクト記法を禁止してしまうことも効果的でしょう。それを実現するのが no feature "indirect" です。

Perl 5.32 現在、通常は use feature "indirect" せずとも間接オブジェクト記法は有効な文法となっています。

#use feature qw(indirect);
#no feature qw(indirect);
use HTTP::Tiny;

# Perl 5.32 現在、feature で indirect が言及されていない場合、間接オブジェクト記法は有効な文法
my $ua = new HTTP::Tiny; # 問題なく HTTP::Tiny->new と同じ意味として解釈される

しかし、 no feature "indirect" することで、間接オブジェクト記法を無効な文法とすることができます。

#!/usr/bin/env perl
use strict;
use warnings;
no feature qw(indirect);
use HTTP::Tiny;

my $ua = new HTTP::Tiny;
__END__
Bareword found where operator expected at tmp/feature-indirect.pl line 7, near "new HTTP::Tiny"
        (Do you need to predeclare new?)

メソッドの後に置かれるものが裸のワードではないスカラー変数でも同様です。

#!/usr/bin/env perl
use strict;
use warnings;
no feature qw(indirect);
use HTTP::Tiny;

my $ua = HTTP::Tiny->new;
my $res = get $ua "http://example.com/";
print $res->{content};

__END__
Scalar found where operator expected at tmp/feature-indirect.pl line 8, near "get $ua"
        (Do you need to predeclare get?)
String found where operator expected at tmp/feature-indirect.pl line 8, near "$ua "http://example.com/""
        (Missing operator before "http://example.com/"?)
syntax error at tmp/feature-indirect.pl line 8, near "get $ua "

この「use feature 時がデフォルトで、no feature することで禁止することができる」挙動は feature では arybase と同じ扱いです。

将来の Perl(たぶん Perl 7)で、間接オブジェクト記法がデフォルトで無効となることもあるかもしれません。そのときは use feature "indirect" とすることで後方互換を維持するために間接オブジェクト記法を局所的に有効にすることができる含みも持たせていると考えると興味深いです(筆者の妄想です)。

なお、 print 等でファイル出力する際の print FH "hello\n"print $fh "hello\n"といったファイルハンドルを print に渡す構文も上記の間接オブジェクト記法と同じ扱いになるはずなのですが、この print の呼び出しは no feature "indirect" の禁止対象外となっているようです。 print はコアの関数でもあり、ここの文法変更は影響が大きいと判断されたのかもしれません。

multidimensional - 多次元配列のエミューレーションの許可または禁止

mutlidimensional を使うと、Perl 4 までのハッシュを使った多次元配列のエミューレーションを許可したり禁止することができます。

use feature qw(multidimensional);

my %two_dim_matrix;
$two_dim_matrix{0, 0} = "a";
$two_dim_matrix{0, 1} = "b";
$two_dim_matrix{1, 0} = "c";
$two_dim_matrix{1, 1} = "d";

my ($x, $y) = (1, 1);
print $two_dim_matrix{ $x, $y } . "\n"; # => "d"
print $two_dim_matrix{ 0, 0 } . "\n"; # => "a"

Perl 5.34 現在、通常は use feature "multidimensional" せずとも上記のハッシュによる多次元配列エミュレーションは有効な文法となっています。

上述の通り、この多次元配列のエミューレーションは Perl 4 までのもので、Perl 5 からは普通に配列リファレンスを使うべきでしょう。

my @two_dim_matrix;
$two_dim_matrix[0][0] = "a";
$two_dim_matrix[0][1] = "b";
$two_dim_matrix[1][0] = "c";
$two_dim_matrix[1][1] = "d";
# または
# my @two_dim_matrix = (
#     ["a", "b"],
#     ["c", "d"]
# );

my ($x, $y) = (1, 1);
print $two_dim_matrix[$x][$y] . "\n"; # => "d"
print $two_dim_matrix[0][0] . "\n"; # => "a"

Perl 5 から一般的となった配列リファレンスによる多次元構造の使用を推奨するため、Perl 4 までの多次元配列エミュレーションを禁止する目的として multidimensional の否定をするという方がメインの使い方と思います。

#!/usr/bin/env perl
use strict;
use warnings;
no feature qw(multidimensional);

my %two_dim_matrix;
$two_dim_matrix{0, 0} = "a";
$two_dim_matrix{0, 1} = "b";
$two_dim_matrix{1, 0} = "c";
$two_dim_matrix{1, 1} = "d";

__END__
Multidimensional hash lookup is disabled at /Users/tetsuji.ogata/tmp/feature-multidimensional.pl line 7, near "0}"
Multidimensional hash lookup is disabled at /Users/tetsuji.ogata/tmp/feature-multidimensional.pl line 8, near "1}"
Multidimensional hash lookup is disabled at /Users/tetsuji.ogata/tmp/feature-multidimensional.pl line 9, near "0}"
Multidimensional hash lookup is disabled at /Users/tetsuji.ogata/tmp/feature-multidimensional.pl line 10, near "1}"
(以下略)

bareword_filehandles - 裸のワードのファイルハンドルの許可または禁止

bareword_filehandles を使うと、裸のワードのファイルハンドルを許可したり禁止することができます。

use feature qw(bareword_filehandles);

open FH, '<', "$ENV{HOME}/tmp/sample-file.txt";
while (<FH>) {
    print ">>> $_";
}

Perl 5.34 現在、通常は use feature "bareword_filehandles" せずとも裸のワードのファイルハンドルは有効な文法となっています。

レキシカルスコープの恩恵を受けたりするため、現代では Perl 5 で導入されたスカラー変数のファイルハンドルを使用すべきでしょう。

open my $fh, '<', "$ENV{HOME}/tmp/sample-file.txt";
while (<$fh>) {
    print ">>> $_";
}

Perl 5 から一般的となったレキシカル変数のファイルハンドルの使用を推奨するために裸のワードによるファイルハンドルを禁止する目的として、bareword_filehandles の否定をするという方がメインの使い方と思います。

#!/usr/bin/env perl
use strict;
use warnings;
no feature qw(bareword_filehandles);

open FH, '<', "$ENV{HOME}/tmp/sample-file.txt";
while (<FH>) {
    print ">>> $_";
}
__END__
Bareword filehandle "FH" not allowed under 'no feature "bareword_filehandles"' at /Users/tetsuji.ogata/tmp/feature-bareword_filehandles.pl line 6.
Bareword filehandle "FH" not allowed under 'no feature "bareword_filehandles"' at /Users/tetsuji.ogata/tmp/feature-bareword_filehandles.pl line 7.

なお、Perl 標準で存在する以下の裸のワードによるファイルハンドルは、 no feature "bareword_filehandles" しても使うことが可能です。

  • STDIN
  • STDOUT
  • STDERR
  • DATA
  • ARGV
  • ARGVOUT
  • _

ARGV および ARGVOUT は perlvar、DATA は perldata を参照下さい。また _$_ ではなく)は -X ファイルテスト関数、 lstat 関数、 stat 関数で使われる特別なファイルハンドルです。詳細は stat関数の解説 を参照下さい。

try - try/catch構文の導入

try を使うと、多くのプログラミング言語でおなじみの try/catch 構文を導入することができます。

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw(try);

try {
    print "自然数を入力して下さい: ";
    chomp( my $input = <STDIN> );
    if ( $input !~ /^\d+$/ ) {
        die "Not Number\n";
    }
} catch($e) {
    print "エラーが発生しました:$e\n";
    # catch 中で die で例外終了したり exit でプロセス終了しなければ、処理は続く
}
print "終了します\n";

ただし、Perl 5.34 現在、そのままでは

try/catch is experimental

といった警告が出てきてしまいます。理解して使っているのでこれを抑止したいという場合には

no warnings "experimental::try";

と書きます。

前述の通り、try/catch は多くのプログラミング言語でおなじみのもので、Perl 5.34 より以前の Perl からこの構文を実現するモジュールが多数作成されました。よく聞くものとして Try::Tiny などがあります。しかし、今までの try/catch 導入モジュールは制御構文として try/catch を導入することができず、文の区切りとしてブロック終わりに ; が必要だったり、 try 中での return の挙動の制限など、色々な制約がありました。このあたりは Try::Tiny の注意書き (CAVEATS) などが参考になります。

defer - defer構文の導入

defer を使うと、ブロックから抜けるときに実行したい処理を書くことができます。

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw(say defer);
no warnings qw(experimental::defer);
 
# 無名ブロック
{
    say "最初に表示される";
    defer { say "最後に表示される"; }
 
    say "そしてこれは中間で表示される";
}

上から下にコードを読んでいって、同じスコープに複数の defer がある場合、最後に見つけたものから順番に実行されます。つまり LIFO です。

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw(say defer);
 
# 無名ブロック
{
    say "こんにちは";
    defer { say "defer 3rd"; }
    defer { say "defer 2nd"; }
    defer { say "defer 1st"; }
 
    say "じゃあね〜";
}

my などのレキシカルスコープであれば何でも大丈夫です。

もともと、このようなスコープを抜ける直前に処理を差し込みたい場合、Scope::GuardGuard などのモジュールを use することで使える my $guard = scope_guard { ... }; を使うことで行っていました。Perl 5.36 より以前の Perl を対象に defer のような処理を行いたい場合はこれらモジュールの使用も検討下さい。

ただし、Perl 5.36 現在、そのままでは

defer is experimental

といった警告が出てきてしまいます。理解して使っているのでこれを抑止したいという場合には

no warnings "experimental::defer";

と書きます。

注意点は perlsyn が詳しいです。

extra_paired_delimiters - qrなどで対として使える括弧文字を多数導入

Perl では、各種リテラル(プログラム内に直接書かれる情報)を書く際にデリミタを選ぶことができる構文があります。

前置する英字 リテラルの意味 使用例 備考
q シングルクォート文字列 q/STRING/ 'STRING' と同じ
qq ダブルクォート文字列 qq/STRING/ "STRING" と同じ
qr 正規表現オブジェクト qr/^\d\d\d\d/
qx コマンド実行 qx/uptime/ `uptime` と同じ
qw 単語文字列のリスト作成 (qw/apple orange banana/) ("apple", "orange", "banana") と同じ
tr, y 置換文字リスト
m 正規表現マッチ
s 正規表現置換

詳細は perlop のクオート風演算子 を参照下さい。

上記クオート風演算子は、ASCII キーボードで入れることができる括弧ペアであれば、そのペアで定義することができます。

my @fruits = (qw[apple orange banana]); # [] は qw のクオートであって、配列リファレンスとは無関係
my $name_re = qr{^a}; # {} は qr のクオートであって、他の文法の波括弧とは無関係
for my $one_fruits (@fruits) {
    say $one_fruits if $one_fruits =~ /$name_re/;
}

Perl 5.34 までだと、この括弧対として選ぶことができたのは、ASCII キーボードから入力可能な UTF-8 でも1バイトな文字のみでした。具体的には以下

  • 丸括弧 (round bracket) ()
  • 角括弧 (square bracket) []
  • 波括弧 (brace) {}
  • 小なり大なり記号を angle bracket と見立てたもの <>

Perl 5.36 の extra_paired_delimiters を使うと、UTF-8 でマルチバイトとして定義された様々な括弧対とみなせる記号をクオート風演算子で使うことができます。その種類はあまりに多いので、公式ドキュメントを参照下さい。

注意点として、括弧対として使う文字がバイト列でなく文字列の1文字であることを示すため、 use utf8 は必須となります。それに伴って、ソースコードに書いた日本語文字列もバイト列ではなく(Perlの内部)文字列になるため、print や say など外部へ出力する際にはバイト列に戻してやる必要があります(下記例では binmode STDOUT, ':utf8' で行っています)。

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw(say extra_paired_delimiters);
no warnings qw(experimental::extra_paired_delimiters);
use utf8;
binmode STDOUT, ':utf8';

my $string = qq「セリフみたい!だけど出力時に括弧は出ないけれどね」;

say $string;

ただし、Perl 5.36 現在、そのままでは

Use of '「' is experimental as a string delimiter

といった警告が出てきてしまいます。理解して使っているのでこれを抑止したいという場合には

no warnings "experimental::extra_paired_delimiters";

と書きます。

  1. 例年、だいたい5月頃です。アップデート日は Wikipedia の Perl の記事によくまとまっています。

  2. リストをともなわず use feature; した場合、 No features specified エラーとなります。これは警告ではなく例外です。

  3. 実行 perl インタプリタ以上のバージョンを use feature ":5.マイナーバージョン"; 指定しても例外終了するので同じ効果となりますが、use v5.マイナーバージョン; の方がインタプリタバージョンを制限している意図が伝わりやすいと思います。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31