Perl/制御構造
この項目では、Perlの制御構造について説明します。
ループ[編集]
以下の説明で、EXPRは式、BLOCKはコードブロック、VARは変数、LISTはリスト、LABELはラベルを示します。 また、大カッコ([])で囲まれたものは省略可能です。
for[編集]
- コード例
#!/usr/bin/perl use strict; use warnings; for ( my $i = 0 ; $i < 5 ; $i++ ) { print "Hello $i\n"; }
- 実行結果
Hello 0 Hello 1 Hello 2 Hello 3 Hello 4
- my $i = 0が評価され、ループ変数$iが初期化されます。
- $i < 5が評価されます。真であればブロックが実行され、文字列が出力されます。偽であればその時点でループを終了します。
- $i++が評価され、$iの値が1増加します。
- 2と3を繰り返します。
- 構文
[ 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ブロックを制御する振舞いをします。