メインメニューを開く

Perl/制御構造

< Perl
プログラミング > Perl > 制御構造

この項目では、Perlの制御構造について説明します。

目次

ループ編集

以下の説明で、EXPRは式、BLOCKはコードブロック、VARは変数、LISTはリスト、LABELはラベルを示します。 また、大カッコ([])で囲まれたものは省略可能です。

for編集

構文:

[ LABEL ] for ( [ EXPR ]; [ EXPR ]; [ EXPR ] ) BLOCK

カッコの中に3つの式をコンマで区切って記述します。いずれも省略可能で、すべて省略した場合は無限ループとみなされます。

最初のEXPRは初期化式です。ループの開始時に1回だけ評価されます。主にループ変数の初期化に使われます。

次のEXPRは条件式です。BLOCKが実行される前に毎回評価され、偽となった時点でループは終了します。 この式が最初から偽だった場合、BLOCKは1回も実行されません。

最後のEXPRは継続式です。BLOCKが実行された後に毎回評価されます。主にループ変数を変化させるのに使われます。

for ( $i = 0; $i < 5; $i++ ) { print "Hello $i\n"; }

上の例の実行結果は以下のようになります:

Hello 0
Hello 1
Hello 2
Hello 3
Hello 4

このプログラムの動作を説明すると、次のようになります:

  1. $i = 0が評価され、ループ変数$iが初期化されます。
  2. $i < 5が評価されます。真であればブロックが実行され、文字列が出力されます。偽であればその時点でループを終了します。
  3. $i++が評価され、$iの値が1増加します。
  4. 2と3を繰り返します。

次の例はユーザから入力された数値を合計し、その値を出力するプログラムです('q'が入力されると終了):

$total = 0;
for ( print 'input>'; ( $input = <> ) !~ /^q/i; print 'input>' ) { $total += $input; }
print "total: $total\n";

while, until編集

構文:

[ LABEL ] while ( EXPR ) BLOCK [ continue BLOCK ]
[ LABEL ] until ( EXPR ) BLOCK [ continue BLOCK ]

whileは、EXPRが真である間BLOCKを繰り返し実行します。 untilは、EXPRが偽である間BLOCKを繰り返し実行します。

つまり、 while ( EXPR ) は until ( not EXPR ) と等価です。

for文の例と同等のものは次のようになります:

$i = 0;
while ( $i < 5 ) {
    print "Hello $i\n";
    $i++;
}
$i = 0;
until ( not $i < 5 ) {
    print "Hello $i\n";
    $i++;
}

ただし、これらはループ制御コマンドが使用された場合、for文と厳密には等価ではありません。 continueブロックを設けることで、for文の継続式と等価に扱われるコードを記述できます。

$i = 0;
while ( $i < 5 ) {
    print "Hello $i\n";
} continue {
    $i++;
}

do-while, do-until編集

構文:

do BLOCK while ( LIST )

doブロックにwhileが後置された場合は、まずdoブロックが一度実行されてから、whileの条件部が評価されます。doブロックはループではないので、後述するループ制御コマンドを用いることはできません。LABELを付ける事も出来ません。

my $i = 4;
do {
    print "Hello";
} while ($i--);

whileと同様、untilを後置する事も出来ます。

foreach編集

構文:

[ LABEL ] foreach [ VAR ] ( LIST ) BLOCK [ continue BLOCK ] 

リストの値を順番にVARに代入し、BLOCKを実行します。 VARはループ内のlocal変数とみなされるので、プログラムの他の場所で使用していても問題ありません。 VARを省略した場合、local変数の$_が使われます。 ループ内をスコープとするmy変数を使用したい場合、次のようにします:

foreach my $i ( 0..4 ) { print "Hello $i\n"; }

これもfor文の例と同等ですが、while文、until文の例と同様に厳密には等価ではありません。 foreachにもcontinueブロックを設けることが出来ます。

foreachは実のところforの同義語であり、相互に置き換えることが出来ます。 一行プログラム(ワンライナー)などでは、foreachとしてforを使うことがしばしばあります。

条件分岐編集

if, elsif, else, unless編集

構文:

if ( EXPR ) BLOCK [ elsif ( EXPR ) BLOCK [ elsif ... ] ] [ else BLOCK ]
unless ( EXPR ) BLOCK [ elsif ( EXPR ) BLOCK [ elsif ... ] ] [ else BLOCK ]

条件分岐のための構文です。

ifは、EXPRが真であればBLOCKを実行しま す。 unlessは、EXPRが偽であればBLOCKを実行します。 つまり if ( EXPR ) は unless ( not EXPR ) と等価です。

elsifは、先に置かれたif/unless/elsifのBLOCKが実行されなかった場合にEXPRを評価し、真であればBLOCKを実行します。 elsifは複数設けることが出来ます。

unlessとelsifを組み合わせると、分岐の判断基準が不統一になって読解性が良くないため、 elsifが必要な場合は、 unless ( EXPR ) ではなく if ( not EXPR ) を使った方が良いという見方もあります。

elseは、先に置かれたif/unless/elsifのBLOCKが実行されなかった場合にBLOCKを実行します。

$a = 5;
if ( $a > 1 ) { print '$aの値は1より大きい'; }
$a = 5;
unless ( $a <= 1 ) { print '$aの値は1より大きい'; }
$a = 5;
if ( $a > 1 ) {
 print '$aの値は1より大きい';
} else {
 print '$aの値は1以下';
}
$a = 5;
if ( $a > 3 ) {
 print '$aの値は3より大きい';
} elsif ( $a > 1 ) {
 print '$aの値は1より大きい';
} else {
 print '$aの値は1以下';
}

文修飾子編集

for/elsif/elseを除く制御構文は、文修飾子としても使用できます。 一文のみのごく簡単な制御の場合はこちらのほうが簡潔で、英文のような見た目になります。

print "Good morning.\n" if 6 <= $hour and $hour < 12;

ループ制御コマンド編集

ループ制御を行なうコマンドを説明します。前出のdo-while以外のループ構文と空のブロックでは、ループの挙動を制御するコマンドが各種使用出来ます。例外として、continue BLOCK 中でそれらのループ制御コマンドを使用した場合には、直近のループブロックを制御する振舞いをします。


ラベルを指定することで入れ子になったループの制御もできます。省略した場合、もっとも内側にあるループを指定したとみなされます。

last編集

即座にループから脱出します。continueブロックは実行されません。

OUTER: while ( 1 ) { #無限ループ
    $i = 0;
    INNER: while ( $input = <> ) {
        last       if $input =~ /^restart/; #INNERループを脱出
        last OUTER if $input =~ /^quit/;
        print "$i\n";
    } continue {
        $i++;
    }
}

next編集

残りの文をスキップし、次の反復に移ります。 ループ条件式は評価されます。continueブロック、for文の継続式も評価されます。

redo編集

残りの文をスキップし、反復をやりなおします。 ループ条件式は評価されません。continueブロック、for文の継続式も評価されません。

continue BLOCK編集

continue BLOCK中の制御の例を上げます。

$i = 0;
{                        #1.
    print "OUTER\n";
    {                    #2.
        $i ++ ;
        print "INNER\n" ;
    } continue {
        redo if $i < 10 ;
        print "$i\n"
    }
}

この場合のredoは#1のブロックではなく、直近の#2ブロックを制御する振舞いをします。