Perl/制御構造
制御構造 編集
この項目では、Perlの制御構造について説明します。
- 以下の構文説明では、EXPRは式、BLOCKはコードブロック、VARは変数、LISTはリスト、LABELはラベルを示します。
- 角括弧 [ ] で囲まれたものは省略可能です。
- 波括弧 { } で囲まれたものは0回以上の繰返しが可能です。
条件分岐 編集
- かつて given/when/default 構文の実験的実装がありましたが、Perl6(現在のRaku)のテストベッドに使っただけらしく、幾つかのバグが発見され、Perl-5.24 ではレキシカルな $_ の仕様がキャンセルされるなど、言語コアにも取り込まれていないので、ここでは扱いません。
if 編集
if文によって、ある条件をみたしているかを判定し、判定の内容により動作を切り替えることができます。
- 例
#!/usr/bin/perl use v5.12; use warnings; my $x = 5; if ($x > 1) { say '$xの値は1より大きい'; }
- 実行結果
$xの値は1より大きい
- 上記の例の場合、変数 $x が 1 より大きいかどうかの区別を判定しています。
- 複数行の例
#!/usr/bin/perl use v5.12; use warnings; my $x = 5; if ($x > 1) { say '$xの値は1より大きい'; }
- のように改行して書いても、構いません。
else 編集
elseは「さもなければ」という意味で、直前のif文の条件が満たされていない場合の処理を担当します。
elseのないifはありますが、ifのないelseはありえません(else節はif構文の構成要素の1つです)。
- 例
#!/usr/bin/perl use v5.12; use warnings; my $x = 5; if ($x > 9) { say '$xの値は9より大きい'; } else { say '$xの値は9以下'; }
- 実行結果
$xの値は9以下
elsif 編集
elsif ( 式 ) BLOCK
は、else { if ( 式 ) BLOCK }
の構文糖です。
なお、elif
でもelseif
ではなくelsif
です。
- 例
#!/usr/bin/perl use v5.12; use warnings; my $x = 2; if ($x > 3) { say '$xは3より大きい'; } elsif ($x > 1) { say '$xは4より大きい'; } else { say '$xは1以下'; }
- 実行結果
$xの値は4より大きい
- 先行するifの条件が成立せず、elsifの条件式が成立しているのでelsifの BLOCK が実行されます(そしてelseのBLOCKは実行されません)。
- 例
#!/usr/bin/perl use v5.12; use warnings; my $x = 5; if ($x > 3) { say '$xの値は3より大きい'; } elsif ($x > 4) { say '$xの値は4より大きい'; } else { say '$xの値は1以下'; }
- 実行結果
$xの値は3より大きい
- 先行するifの条件がしているのifのBLOCKが実行され、elsifの条件式の成否にかかわらずelsifの BLOCK は実行されません(そしてelseのBLOCKも実行されません)。
- 構文
if ( EXPR ) BLOCK { elseif (EXPR) BLOCK }* [ else BLOCK ]
unless 編集
英語でunlessは「もし…でなければ」という意味です。
Perlのunlessは、EXPRが偽の場合、BLOCKを実行します。
- 例
#!/usr/bin/perl use v5.12; use warnings; my $x = 5; unless ($x <= 1) { say '$xの値は1以下でない'; }
- 実行結果
$xの値は1以下でない
- 構文
unless ( EXPR ) BLOCK { elsif (EXPR) BLOCK }* [ else BLOCK ]
- unless にも任意個のelsif節と任意のelse節が後続可能です。
- else if ⇒ elsif のような、else unless ⇒ elsunless はありません。
ループ 編集
Perlの繰返し構文には、for,while, until,do-while, do-until,foreachと基本ブロックがあります。 forとforeachはシノニムの関係にありますが、ここではループカウンターを使ったC風の繰返し構文をfor、リスト・配列やハッシュの要素を対象にイテレートする構文をforeachとしました。
for 編集
Perlには、Cのfor文風の繰返し構文があります。
- コード例
#!/usr/bin/perl use v5.12; use warnings; for (my $i = 0; $i < 5; $i++) { say "Hello $i" } # say $i; # $i のスコープは for ループ内なのでここで参照すると、グローバル変数 $i の参照になります。
- 実行結果
Hello 0 Hello 1 Hello 2 Hello 3 Hello 4
- my $i = 0が評価され、レキシカルスコープのループ変数$iが初期化されます。
- $i < 5が評価されます。真であれば BLOCK が実行され、文字列が出力されます。偽であればその時点でループを終了します。
- $i++が評価され、$iの値が1増加します。
- 2と3を繰り返します。
- 構文
[ LABEL ] for ( [ EXPR ]; [ EXPR ]; [ EXPR ] ) BLOCK
- カッコの中に3つの式をコンマで区切って記述します。いずれも省略可能です。
- 最初のEXPRは初期化式です。ループの開始時に1回だけ評価されます。主にループ変数の初期化に使われます。
- ここで my 宣言されたレキシカルスコープのループ変数のスコープはforループの中です。
- 次のEXPRは条件式です。BLOCKが実行される前に毎回評価され、偽となった時点でループは終了します。
- この式が最初から偽だった場合、BLOCKは1回も実行されません。(初期化式は実行されます)
- 条件式を省略した場合、真が仮定され無限ループとなります。
- 最後のEXPRは継続式です。BLOCKが実行された後に毎回評価されます。主にループ変数を変化させるのに使われます。
while 編集
while文は、EXPRが真である限り、ブロックを実行し続けます。
for文の例と同等のものは次のようになります(厳密には $i のスコープがループを抜けた後も続くところが違います)。
- 例
#!/usr/bin/perl use v5.12; use warnings; my $i = 0; while ($i < 5) { say "Hello $i"; $i++; }
- 実行結果
Hello 0 Hello 1 Hello 2 Hello 3 Hello 4
while w/ continue BLOCK 編集
continueブロックをfor文の継続式をイミュレーションできます。
- 例
#!/usr/bin/perl use v5.12; use warnings; my $i = 0; while ($i < 5) { say "Hello $i"; } continue { $i++; }
- 実行結果
Hello 0 Hello 1 Hello 2 Hello 3 Hello 4
- 構文
[ LABEL ] while ( EXPR ) BLOCK [ continue BLOCK ]
- whileは、EXPRが真である間BLOCKを繰り返し実行します。
until 編集
untilは、EXPRが偽である限り、ブロックを実行し続けます。
for文の例と同等のものは次のようになります(厳密には $i のスコープがループを抜けた後も続くところが違います)。
- 例
#!/usr/bin/perl use v5.12; use warnings; my $i = 0; until($i >= 5) { say "Hello $i"; $i++; }
- 実行結果
Hello 0 Hello 1 Hello 2 Hello 3 Hello 4
until w/ continue BLOCK 編集
continueブロックをfor文の継続式をイミュレーションできます。
- 例
#!/usr/bin/perl use v5.12; use warnings; my $i = 0; until ($i => 5) { say "Hello $i"; } continue { $i++; }
- 実行結果
Hello 0 Hello 1 Hello 2 Hello 3 Hello 4
- 構文
[ LABEL ] until ( EXPR ) BLOCK [ continue BLOCK ]
- untilは、EXPRが偽である間BLOCKを繰り返し実行します。
- つまり、 while ( EXPR ) は until ( ! EXPR ) と等価です。
do-while 編集
doブロックにwhileが後置された場合は、まずdoブロックが一度実行されてから、whileの条件が真である間繰り返します。 doブロックはループではないので、後述する実行制御文を用いることはできません。LABELを付ける事も出来ません。
- 構文
do BLOCK while ( EXPR ) ;
- 例
#!/usr/bin/perl use v5.12; use warnings; my $i = 4; do { say $i; } while ($i--);
- 実行結果
4 3 2 1 0
- 上に構文と書きましたが、実際は
EXPR1 while EXPR2
の EXPR1 がdo BLOCK
になった複合構文です。
do-until 編集
doブロックにuntilが後置された場合は、まずdoブロックが一度実行されてから、untilの条件が偽である間繰り返します。 doブロックはループではないので、後述する実行制御文を用いることはできません。LABELを付ける事も出来ません。
- 構文
do BLOCK until ( EXPR ) ;
- 例
#!/usr/bin/perl use v5.12; use warnings; my $i = 4; do { say $i; } until ($i-- <= 0);
- 実行結果
4 3 2 1 0
- 上に構文と書きましたが、実際は
EXPR1 while EXPR2
の EXPR1 がdo BLOCK
になった複合構文です。
foreach 編集
- 構文
[ LABEL ] foreach [ VAR ] ( LIST ) BLOCK [ continue BLOCK ]
- リストの値を順番にVARに代入し、BLOCKを実行します。
- VARはループ内のlocal変数とみなされるので、プログラムの他の場所で使用していても問題ありません。
- VARを省略した場合、local変数の$_が使われます。
- ループ内をスコープとするmy変数を使用したい場合、次のようにします:
foreach my $i ( 0..4 ) { say "Hello $i"; }
- これもfor文の例と同等ですが、while文、until文の例と同様に厳密には等価ではありません。
- foreachにもcontinueブロックを設けることが出来ます。
foreachはforのシノニム 編集
foreachはforのシノニムで、相互に置換えることが出来ます。 一行プログラム(ワンライナー)などでは、foreachとしてforを使うことがしばしばあります。
基本ブロック 編集
{
と }
に囲まれた基本ブロック( Basic block )が、ループの節にあるのは奇異に感じるかもしれませんが、Perl では基本ブロックは「1周しかしないループ」で、ループ制御文を使うことができます。
doブロック 編集
doブロックは基本ブロックではなく、ブロックの最後の式の値を返す式です。 doブロックが式だからこそ、while 文修飾子 や until 文修飾子 とも結合できるのです(やる気になれば、if/until/foreach文修飾子とも結合できます)。 doブロックでは、ループ制御文を使うことはできません。
- doブロックは式
#!/usr/bin/perl use v5.12; use warnings; my $x = do { my $i = 100; $i+100 }; say $x
- 実行結果
200
- do と next の組合わせ
do {{ next if $x == $y; ... }} until $x++ > 0;
- doブロックの {} の内側に、基本ブロックの {} を入れます。
- do と last の組合わせ
{ do { last if $x == $y; ... } while $x++ <= 0 }
- doブロックの {} の外側を、基本ブロックの {} で囲みます。
defer 編集
Perl 5.36.0 で、待望の defer が追加されました。
- deferで登録されたブロックは、スコープを抜けたときに実行されます
use v5.36.0; use feature 'defer'; no warnings "experimental::defer"; say "Befor"; { defer{ say "One" } defer{ say "Two" } defer{ say "Three" } say "Done!" } say "After";
- 実行結果
Befor Done! Three Two One After
- ブロックは、登録した逆順に実行されます。
色々な用途が考えられますが、open で開いたファイルハンドルの close 処理を defer で登録する使い方が真っ先に思いつきます。
真理値 編集
Perlの真理値のルールはやや難解です。
- 数値
- 0は偽、それ以外は真。
- 文字列
- ""(空文字列)あるいは"0"(数値0に暗黙変換される)は偽、それ以外は真。
- リスト
- ()(空リスト)は偽、それ以外は真(これはコンテキストにも影響され、スカラコンテキストにリストを渡すと要素数になり要素ゼロの場合、数値0と評価され偽となります)。
- ハッシュ
- 要素数ゼロのハッシュは偽、それ以外は真(これはコンテキストにも影響され、スカラコンテキストにハッシュを渡すと要素数になり要素ゼロの場合、数値0と評価され偽となります)。
- undef
- 偽
多くの説明では、数値にしか言及していませんが、他にも条件式で「偽」と評価される値があります。特に文字列には注意してください。
- Perlのコード例
my $x = 1; if ($x) { print $x . 'は真'; } else { print $x . 'は偽'; }
- 結果
1は真
- 0の真理値
my $x = 0; if ($x) { print $x . 'は真'; } else { print $x . 'は偽'; }
- 結果
0は偽
- (空文字列)や’0’も偽
my @ary = (0, 1, 2, 3, '', "0", " ", "1", "2", (), (1), undef); foreach my $x(@ary) { if ($x) { print "($x)は真 "; } else { print "($x)は偽 "; } }
- 実行結果
(0)は偽 (1)は真 (2)は真 (3)は真 ()は偽 (0)は偽 ( )は真 (1)は真 (2)は真 (1)は真 ()は偽
constant プラグマを使った真理値定数の定義 編集
- Perl 5.36.0 からは
use builtin qw(true false)
で、真理値定数 true と false が定義されたので、constant プラグマを使った真理値定数の定義は Obsolate になりました。また、builtin::is_bool()
も 5.36.0 で追加されました。
[TODO:節を改めての builtin の解説]
Perlには、他のいくつかのプログラミング言語にある、真理値定数 true と false がありませんが、constant プラグマを使うことで実現できます[1]。
- constant プラグマを利用
use constant { false => 0 != 0, true => 0 == 0 }; my @ary = (false, true); foreach my $a(@ary){ if ( $a ) { print "($a)は真 "; } else { print "($a)は偽 "; } }
- 実行結果
()は偽 (1)は真
0 != 0
が 0 ではなく ”” というのは意外ですね。
文修飾子 編集
次の制御構文は、文修飾子( Statement Modifiers )としても使用できます[2]。
- 構文
EXPR if EXPR EXPR unless EXPR EXPR while EXPR EXPR until EXPR EXPR foreach [ VAR ] LIST
- 一文のみのごく簡単な制御の場合はこちらのほうが簡潔で、英文のような見た目になります。
- 修飾子が後になったほか、条件式を囲む括弧 ( ) が不要などの差異があります。
- Perlの(文修飾子でない)制御構文には { } が必須なのですが、文修飾子では逆に使用不可です。
- Cになれた人には、単文・複文の関係にみえますが、Perlの構文は、文:=単文 | 複文 ではなく制御構文ごとにブロックを要求したり式を要求したりします。
- 条件文
print "Good morning.\n" if 6 <= $hour and $hour < 12;
- 繰返し文
print "@{[$i++]} " while $i < 10; print "<$_> " foreach qw(abc def xyz); print "\n----\n"; $i = 0; while ($i < 10) { print "@{[$i++]} " } foreach (qw(abc def xyz)) { print "<$_> "; }
- 実行結果
0 1 2 3 4 5 6 7 8 9 <abc> <def> <xyz> ---- 0 1 2 3 4 5 6 7 8 9 <abc> <def> <xyz>
ループ制御文 編集
ループ制御を行なう実行制御文の説明をしたいと思います。 前出のdo-while以外のループ構文と空のブロックでは、ループの挙動を制御する各種実行制御文が使用出来ます(doブロックでループ制御文を使いたい場合、基本ブロックを併用します)。 例外として、continue BLOCK 中でそれらの実行制御文を使用した場合には、直近のループブロックを制御する振舞いをします。 ラベルを指定することで入れ子になったループの制御もできます。省略した場合、もっとも内側にあるループを指定したとみなされます。
last 編集
即座にループから脱出します。continueブロックは実行されません。
OUTER: while ( 1 ) { #無限ループ $i = 0; INNER: while ( $input = <> ) { last if $input =~ /^restart/; # 直近のINNERループを脱出 last OUTER if $input =~ /^quit/; # 明示されたOUTERループを脱出 print "$i\n"; } continue { $i++; } }
next 編集
残りの文をスキップし、次の反復に移ります。 ループ条件式は評価されます。continueブロック、for文の継続式も評価されます。
redo 編集
残りの文をスキップし、反復をやりなおします。 ループ条件式は評価されません。continueブロック、for文の継続式も評価されません。
continueブロック 編集
continueブロック中の制御の例を上げます。
my $i = 0; { #1. print "OUTER\n"; { #2. $i ++ ; print "INNER\n" ; } continue { redo if $i < 10 ; print "$i\n" } }
この場合のredoは#1のブロックではなく、直近の#2ブロックを制御する振舞いをします。
- ^ [https://perldoc.perl.org/constant constant - Perl pragma to declare constants ]
- ^ Statement Modifiers