Perl/制御構造

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

プログラミング > Perl > Perl/制御構造 この項目では、Perlの制御構造について説明します。

ループ[編集]

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

for[編集]

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";


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

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

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

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

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

while, until[編集]

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

while文は、つづく丸カッコの中の条件が真であるかぎり、つづく曲がりカッコ { } の中の命令を、回数の制限なく、繰り返しつづけます。

ただし実用的には、大きめの数を繰り返したら終了するようにプログラムをする事が、バグ発生時にマシンの資源を占有しないようにする等の理由で、(有限回での使用が慣習的に)推奨されています。


コード例
$i = 0;
while ( $i < 5 ) {
    print "Hello $i\n";
    $i++;
}


実行結果
Hello 0
Hello 1
Hello 2
Hello 3
Hello 4


コード例

until ( not 条件式 ) はつまり、二重否定です。

until 文は、条件式が満たされない場合にブロックを実行する内容です。

また、notは、つづく条件式の反対です。つづく条件式Aが単独では「真」なら、 not A は「偽」です。つづく条件式Aが単独では「偽」なら、 not A は「真」です。


なので、否定の内容がuntil not と2回続いてるので、これは while文と同等です。


$i = 0;
until ( not $i < 5 ) {
    print "Hello $i\n";
    $i++;
}
実行結果
Hello 0
Hello 1
Hello 2
Hello 3
Hello 4


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

$i = 0;
while ( $i < 5 ) {
    print "Hello $i\n";
} 
continue {
    $i++;
}
実行結果
Hello 0
Hello 1
Hello 2
Hello 3
Hello 4


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

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

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


do-while, do-until[編集]

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

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


実行結果
HelloHelloHelloHelloHello


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

構文
do BLOCK while ( LIST )


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[編集]

if文によって、ある条件をみたしているかを判定し、判定の内容により動作を切り替えることができます。

$a = 5;
if ( $a > 1 ) { print '$aの値は1より大きい'; }

上記の例の場合、変数 $a が 1 より大きいかどうかの区別を判定しています。

if (条件) { 条件達成時の実行処理;}

の書式です。


条件が満たされていない場合には、つづく{ } ブロック内の命令は無視されます。

$a = 5;
if ( $a > 1 ) { 
    print '$aの値は1より大きい'; 
}

のように改行して書いても、構いません。


else および elsif[編集]

else

elseは「さもなければ」というような意味で、直前のif文の条件が満たされていない場合の処理を担当します。

なので、elseには、必ずその前にif文があります。if無しで単体でelseが存在することは無いです。実際、むりやりelse単体でコードを書いてもエラーになるだけです。

コード例
$a = 5;
if ( $a > 9 ) {
  print '$aの値は1より大きい';
} 
else {
  print '$aの値は9以下';
}

実行結果

$aの値は9以下


elsif

elsif は、直前のifの条件が満たされない場合であり、さらに elsif で指定した条件が満たされた場合にだけ、そのelsifの { } ブロックを実行します。

なので、もしelsifの条件だけが満たされていても、直前のifのブロックも満たされている場合にも、elsifのブロックは実行されないです。


なお、「elseif」 ではなく 「elsif」です(真ん中のeが無い)。もし「 elseif 」でコードを記述しても、実行時にエラーになります。

またなお、C言語には else if という2語に分かち書きされた条件分岐の構文がありますが、しかしPerlでは else if と書いてもエラーになります。 Perlでこの機能を使う場合には必ず elsif と書く必要があります。


コード例
$a = 2;
if ( $a > 3 ) {
  print '$aの値は3より大きい';
} 
elsif ( $a > 1 ) {
  print '$aの値は4より大きい';
} 
else {
  print '$aの値は1以下';
}
実行結果
$aの値は1より大きい


解説

このようにすれば、elsifのブロックは実行されます(直前のif文の条件が満たされていないので)。


いっぽう、下記のコードでは、elsifのブロックは実行されないです(直前のif文の条件が満たされているので)。

コード例
$a = 5;
if ( $a > 3 ) {
  print '$aの値は3より大きい';
} 
elsif ( $a > 4 ) {
  print '$aの値は4より大きい';
} 
else {
  print '$aの値は1以下';
}


実行結果
$aの値は3より大きい
解説

$a = 5 なので、elsifの直後にある丸カッコの条件 ( $a > 4 ) を満たしているのにかかわらず、直前のifの条件 ( $a > 3 ) も満たしてしまっているので、elsifのブロックは実行されないです。

unless[編集]

unlessとは英語で「~~ではないかぎりは」というような意味です。

Perlではunlessは、つづく条件式の否定が真の場合に、つづく { } ブロックを実行する処理です。

コード例
$a = 5;
unless ( $a <= 1 ) { print '$aの値は1より大きい'; }
実行結果
$aの値は1より大きい


後置[編集]

Perlではif文やunless文で条件式を後ろに置くこともできます。(これはC言語には無い、Perl独特の機能です。)

命令 if (条件式);

の書式です、

コード例
$a = 5;
print '$aの値は2より大きい' if ( $a > 2 ) ;
実行結果
$aの値は2より大きい


コード例
$a = 5;
print '$aの値は2より小さい' unless ( $a < 2 ) ;
実行結果
$aの値は2より小さい


1と0[編集]

下記のように条件式が文字列「1」のとき、Perlでは真(しん、true)を意味します。

いっぽう、条件式が文字列「0」のとき、Perlでは偽(ぎ、false)を意味します。


Perlにかぎらず、C言語を含む他の多くのプログラム言語でも、文字列「1」はつねに真であり、文字列0はつねに偽です。

Perlのコード例
if ( 1 ) { 
    print '表示されるかな?'; 
}
結果
表示されるかな?


Perlのコード例
if ( 0 ) { 
    print '表示されるかな?'; 
}
結果
※ 結果は何も表示されません。

のようになります。


なお、数学のブール代数という分野でも、1は真、0は偽、です。

またなお、

if ( "1" ) { 中略 }

if ( "0" ) { 中略 }

のように文字列として1や0を条件式に書いても、上記と同様に真偽を判断します。

つまり、 if ( "1" ) はつねに真です。

無い機能[編集]

Perlに無い機能として、 Perl以外の他のいくつかのプログラム言語にある、下記のダメな例のような true や false などの論理型(ろんりがた)は、Perlには ない です。

なので、

※ ダメな例
if ( false ) {  print '表示されるかな?'; }     # このコードは間違いの例です。

とか書いても、Perlではブロック内のprintを実行してしまいます。


条件式に変数以外のカラでない文字列がある場合、Perlでは真として扱います。

まとめ[編集]

構文
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を実行します。

文修飾子[編集]

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ブロックを制御する振舞いをします。