|
|
Perlプログラム作成の繰り返し処理 |
H.Kamifuji . |
繰り返し処理は、決まった回数や条件を満たすまで同じ処理を繰り返し行う場合に使います。ここでは「for」文や「while」文などの繰り返し処理の使い方について確認します。 当ページでは、Linux CentOS7 の Gnome で動作テストしています。 |
|
繰り返し処理を行う方法として「while」文について確認します。while文は条件式が真の間、ブロック内の処理を繰り返し実行します。まずは書式から確認します。 while (条件式){ 実行する処理1; 実行する処理2; }条件式の箇所には「x == 10」や「y > 15」など関係演算子を使った条件式を記述します。この条件が真(true)となる時に「{」から「}」までのブロック内に記述された文が実行されます。 他のプログラムミング言語では、実行される文が1つの場合はブロック無しで記述できる言語もありますが、Perlでは必ず「{」と「}」で囲ってブロックを記述する必要があります。 実際のプログラム例としては次のようになります。 my $count; $count = 0; while ($count < 2){ print "count = $count \n"; $count ++; } print "以上です";この例を元にwhile文がどのように実行されるのかを確認していきます。 まずwhile文が最初に実行される時点で条件式が一度評価されます。もし条件式が偽(false)だった場合はwhile文は終了して次の処理へ進みます。今回の場合は変数「$count」に格納されている値が2よりも小さいかどうかなので、条件式は真(true)となります。真の場合にはブロック内に記述された処理を順に1回実行します。 1)変数「$count」を初期化し、0を格納 2)while文の条件式を評価 3)条件式が真のため、ブロック内を実行 4)「count = 0」を出力 5)変数「$count」に格納されている値を1だけ増加し1にするブロック内の処理を一度実行したら、改めてwhile文の条件式を評価します。変数「$count」の値は1になっていますので、条件式は引き続き真です。その為、再度ブロック内の処理を実行します。 1)while文の条件式を評価 2)条件式が真のため、ブロック内を実行 3)「count = 1」を出力 4)変数「$count」に格納されている値を1だけ増加し2にするまたwhile文の条件式を評価します。変数「$count」の値は2になっていますので、条件式は偽となります。その為、この時点でwhile文は終了し、while文の次へ進みます。 1)while文の条件式を評価 2)条件式が偽のため、while文は終了 3)「以上です」を出力このようにwhile文は条件式が真の間は何度でもブロック内の処理を繰り返します。その為、ブロック内で条件式が変化するような処理(今回は比較している変数の値を増加)を行わないと、いつまでの条件式の評価は同じになってしまいますので無限にループしてしまうことになります。また条件式がいきない偽になる場合はブロック内は一度も実行されません。 while文は決められた回数だけ繰り返す処理よりも、ブロック内で処理される内容がある条件を満たすまで無限に繰り返すといった処理によく使われます。例えばファイルを最初から最後まで読み込むとか、パスワードが一致するするまで入力を行ってもらうなどの用途に使います。 では簡単なプログラムで確認して見ます。 test1-1.pl サンプルプログラム下記のサンプルを実行してみよう。# while文 use strict; use warnings; use utf8; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; my $count; $count = 0; while ($count < 2){ print "count = $count \n"; $count ++; } print "以上です\n";上記を「test1-1.pl」の名前で保存してから次のように実行して下さい。 ![]() Linux 環境での実行結果は、下記です。シフトJIS で出力されるので nkf -w で UTF-8 に変換しています。 [xxxxxxxx@dddddddddd For]$ perl test1-1_u.pl | nkf -w count = 0 count = 1 以上です [xxxxxxxx@dddddddddd For]$ |
while文と似た制御構造としてuntil文が用意されています。while文が条件式が真の間ブロック内の処理を繰り返すのに対してuntil文は条件式が偽の間ブロック内の処理を繰り返します。まずは書式から確認します。 until (条件式){ 実行する処理1; 実行する処理2; }基本的な動作はwhile文と同じです。まず条件式を評価し、条件式が偽だった場合にはブロック内の処理を行い、そして改めて条件式を評価します。 実際のプログラム例としては次のようになります。 my $count; $count = 0; until ($count >= 2){ print "count = $count \n"; $count ++; } print "以上です";条件式が偽の間繰り返しを行いますので、条件式に否定を使うことでまったく同じ処理をwhile文を使って記述させることが出来ます。 my $count; $count = 0; while (!($count >= 2)){ print "count = $count \n"; $count ++; } print "以上です";until文をあえて使わなくてもwhile文だけでプログラムを記述できますが、場合によってはuntil文を使った方が処理の記述がすっきりする場合があります。 では簡単なプログラムで確認して見ます。 test2-1.pl サンプルプログラム下記のサンプルを実行してみよう。# until文 use strict; use warnings; use utf8; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; my $count; $count = 0; until ($count >= 2){ print "count = $count \n"; $count ++; } print "以上です\n";上記を「test2-1.pl」の名前で保存してから次のように実行して下さい。 ![]() Linux 環境での実行結果は、下記です。シフトJIS で出力されるので nkf -w で UTF-8 に変換しています。 [xxxxxxxx@dddddddddd For]$ perl test2-1_u.pl | nkf -w count = 0 count = 1 以上です [xxxxxxxx@dddddddddd For]$ |
繰り返し処理は様々な用途で使用されますが、指定した回数だけ繰り返したり、数値をある値からある値まで順に変化させたりする利用方法をよく使います。 例えば10回繰り返し処理を行う場合、while文を使えば次のように記述できます。 my $count; $count = 0; while ($count < 10){ # 何かの処理 $count ++; }もちろんこの記述方法でも問題ありませんが、指定した回数だけ繰り返すような場合にはfor文を使うと便利です。for文の書式は次のようになっています。 for (初期化; 条件式; インクリメント){ 実行する処理1; 実行する処理2; }「初期化」の箇所にはfor文が実行される時に一度実行される処理を記述し、条件式には繰り返しを継続するかどうかの条件を記述、そしてインクリメントには1回繰り返しが行われた時に実行される処理を記述します。 for文では色々な記述方法が行われますが、よく使われる記述として条件判別用の変数を1つ用意し、初期化で変数の値を初期化し、条件式では変数に対する条件式を記述し、インクリメントでは変数に格納された値をどのように変化させるのかを記述します。 例えば10回繰り返し処理を行う場合、for文を使えば次のように記述できます。 my $count; for ($count = 0; $count < 10; $count++){ print "count = $count \n"; }for文で記述できることは基本的にwhile文で記述することが可能ですが、if文では初期化や条件式、そして繰り返しの度にどのように値を変化させるのかを1行で表すことができます。指定した回数だけ繰り返すような処理を記述するにはfor文を使うことで簡潔にまた明確に記述することが出来ます。 なお今までの例では初期化で使用している変数の宣言をfor文の前で行っていますが、初期化の箇所で宣言も合わせて行うことが出来ます。 for (my $count = 0; $count < 10; $count++){ print "count = $count \n"; }ただし、この場合はfor文の中で変数「$count」を使用することは出来ますが、for文が終了した後で変数「$count」を参照することはできません。変数の宣言位置と有効範囲については別のページで詳しく確認します。 では簡単なプログラムで確認して見ます。 test3-1.pl サンプルプログラム下記のサンプルを実行してみよう。# for文 use strict; use warnings; use utf8; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; for (my $count = 0; $count < 5; $count++){ print "count = $count \n"; }上記を「test3-1.pl」の名前で保存してから次のように実行して下さい。 ![]() Linux 環境での実行結果は、下記です。シフトJIS で出力されるので nkf -w で UTF-8 に変換しています。 [xxxxxxxx@dddddddddd For]$ perl test3-1_u.pl | nkf -w count = 0 count = 1 count = 2 count = 3 count = 4 [xxxxxxxx@dddddddddd For]$ |
for文の書式は次のようになっていました。 for (初期化; 条件式; インクリメント){ 実行する処理1; 実行する処理2; }初期化、条件式、インクリメントの3つの制御部分を指定し、どのように繰り返しを行うのかを指定するのですが、これらの制御部分は必要無ければ省略することも出来ます。省略する場合は何も記述しません(制御部分の間のセミコロンは必ず記述します)。 例えば条件式に何も書かなかった場合、条件式は常に真(true)となり無限ループとなります。 my $count; for ($count = 0;; $count++){ print "count = $count \n"; }このようなプログラムを記述してはいけませんが、for文は無限ループとしておき、for文の中で実行する処理として繰り返しを抜けるような演算子を記述する場合もあります。 また初期化やインクリメントも必要無い場合は省略することが出来ます。全て省略した場合は例えば次のような記述となります。 my $count = 0; for (;;){ print "count = $count \n"; $count++ }制御部分の一部を省略するのは特殊な記述方法ではありますが、まったく使われないわけでもないので覚えておいて下さい。 |
foreach文はfor文と似た構文です。foreachは値の集合であるリストを指定すると、リストに含まれる値の数だけ順に繰り返しを行います。(リストについては別のページで詳しく見ていきます)。 foreach文の書式は次のようになっています。 foreach 変数 (リスト){ 実行する処理1; 実行する処理2; }リストに含まれる値を指定した変数に順に格納し、その度に繰り返し処理を1回行います。よって繰り返しされる回数はリストに含まれている値の数の分だけ行われます。 例えば3つの値が含まれるリストを使い、リストに含まれる値を順に出力するにはforeachを使って次のように記述できます。 my @fruit = ("りんご", "メロン", "イチゴ"); foreach my $name (@fruit){ print "$name\n"; }リストの値を格納する制御変数はforeach文よりも前で宣言を行ってもいいですし、この例のようにforeach文の中で宣言を行っても構いません。 では簡単なプログラムで確認して見ます。 test5-1.pl サンプルプログラム下記のサンプルを実行してみよう。# foreach文 use strict; use warnings; use utf8; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; my @fruit = ("りんご", "メロン", "イチゴ"); foreach my $name (@fruit){ print "$name\n"; }上記を「test5-1.pl」の名前で保存してから次のように実行して下さい。 ![]() Linux 環境での実行結果は、下記です。シフトJIS で出力されるので nkf -w で UTF-8 に変換しています。 [xxxxxxxx@dddddddddd For]$ perl test5-1_u.pl | nkf -w りんご メロン イチゴ [xxxxxxxx@dddddddddd For]$ for文とforeach文の関係for文とforeach文は実は同じ構文です。for文で制御変数とリストを指定すればforeach文のように動作しますし、foreach文に3つの制御部分を指定すればfor文のように動作します。for文で統一してしまうことも可能ですが、混乱してしまう場合もありますのでリストを使う場合にはforeach文を使うようにしておくといいのではないかと思います。 では簡単なプログラムで確認して見ます。 test5-2.pl サンプルプログラム下記のサンプルを実行してみよう。use strict; use warnings; use utf8; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; my @fruit = ("りんご", "メロン", "イチゴ"); for my $name (@fruit){ print "$name\n"; }上記を「test5-2.pl」の名前で保存してから次のように実行して下さい。 ![]() 今回はfor文に対して制御変数とリストを与えて実行しています。このようにforeach文とfor文は同じ使い方をすることが出来ます。 Linux 環境での実行結果は、下記です。シフトJIS で出力されるので nkf -w で UTF-8 に変換しています。 [xxxxxxxx@dddddddddd For]$ perl test5-2_u.pl | nkf -w りんご メロン イチゴ [xxxxxxxx@dddddddddd For]$ |
foreach文の書式は次のようになっていました。 foreach 変数 (リスト){ 実行する処理1; 実行する処理2; }この中でリストの値を格納する変数を省略することが出来ます。 foreach (リスト){ 実行する処理1; 実行する処理2; }変数を省略した場合には、デフォルト変数として「$_」と言う変数が使用されます。つまり変数が省略された場合は変数として「$_」が指定された場合と同じとなります。 例えば前のページで使用した3つの値が含まれるリストを使い、リストに含まれる値を順に出力する場合で考えてみます。 my @fruit = ("りんご", "メロン", "イチゴ"); foreach (@fruit){ print "$_\n"; }変数が省略されていますので、リストに含まれる各値は変数「$_」を使って取り出すことが出来ます。 では簡単なプログラムで確認して見ます。 test6-1.pl サンプルプログラム下記のサンプルを実行してみよう。# foreach文で制御変数を省略 use strict; use warnings; use utf8; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; my @fruit = ("りんご", "メロン", "イチゴ"); foreach (@fruit){ print "$_\n"; }上記を「test6-1.pl」の名前で保存してから次のように実行して下さい。 ![]() 変数を明示的に指定した場合と使用方法は代わりありません。 Linux 環境での実行結果は、下記です。シフトJIS で出力されるので nkf -w で UTF-8 に変換しています。 [xxxxxxxx@dddddddddd For]$ perl test6-1_u.pl | nkf -w りんご メロン イチゴ [xxxxxxxx@dddddddddd For]$ |
繰り返し処理が行われている時に強制的に繰り返し処理を終了させることが出来ます。「last」演算子を使います。書式は次の通りです。 last;繰り返し処理の中で「last」演算子が実行されるとその時点で繰り返し処理は終了となります。なお終了する繰り返し処理は、「last」演算子が記述されている箇所を含む一番内側の繰り返し処理が対象です。 my $count = 1; while (1){ print "$count\n"; $count++; if ($count > 5){ last; } } print "終了\n";上記の例ではwhile文の条件部分が「1」となっており、このwhile文は無限ループとなります。ただ、繰り返し処理の中でif文を使って条件判断を行い、ある条件となったら「last」演算子を実行してwhile文を終了するようにしてあります。 なおlast演算子によって終了となる繰り返し処理というのは、while文、until文、for文、foreach文の他に裸のブロックが対象となります。if文などは対象外ですので、上記のサンプルでlast演算子が実行された時に終了するのはif文ではなくwhile文となります。 では簡単なプログラムで確認して見ます。 test7-1.pl サンプルプログラム下記のサンプルを実行してみよう。# last演算子 use strict; use warnings; use utf8; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; my $count = 1; while (1){ print "$count\n"; $count++; if ($count > 5){ last; } } print "終了\n";上記を「test7-1.pl」の名前で保存してから次のように実行して下さい。 ![]() Linux 環境での実行結果は、下記です。シフトJIS で出力されるので nkf -w で UTF-8 に変換しています。 [xxxxxxxx@dddddddddd For]$ perl test7-1_u.pl | nkf -w 1 2 3 4 5 終了 [xxxxxxxx@dddddddddd For]$ |
「last」演算子は繰り返し処理を終了させるのに使用しましたが、「next」演算子はその時実行している繰り返しを最後の位置までスキップさせて次の繰り返しに進みます。書式は次の通りです。 next;例えば次の例で考えてみます。 my $count = 0; while ($count <= 5){ $count++; if ($count == 2){ next; } print "$count\n"; #next演算子が実行されるとこの位置に進む } print "終了\n";上記では繰り返し処理の中で条件判断を行い、変数「$count」が「2」と等しい時にはnext演算子が実行されます。next演算子が実行されると繰り返し処理の最後の位置までの処理は全てスキップし、次の繰り返しへ進みます。その為、今回の例では変数「$count」が「2」と等しい時には「print "$count\n";」が実行されません。 next演算子の場合も対象となる繰り返し処理というのは、while文、until文、for文、foreach文の他に裸のブロックが対象となります。if文などは対象外ですので、上記のサンプルでnext演算子が実行された時に繰り返し処理の最後まで進むのはif文ではなくwhile文となります。 では簡単なプログラムで確認して見ます。 test8-1.pl サンプルプログラム下記のサンプルを実行してみよう。# next演算子 use strict; use warnings; use utf8; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; my $count = 0; while ($count <= 5){ $count++; if ($count == 2){ next; } print "$count\n"; #next演算子が実行されるとこの位置に進む } print "終了\n";上記を「test8-1.pl」の名前で保存してから次のように実行して下さい。 ![]() 今回の例では変数の値が「2」の時だけ画面に出力されません。 Linux 環境での実行結果は、下記です。シフトJIS で出力されるので nkf -w で UTF-8 に変換しています。 [xxxxxxxx@dddddddddd For]$ perl test8-1_u.pl | nkf -w 1 3 4 5 6 終了 [xxxxxxxx@dddddddddd For]$ |
「last」演算子や「next」演算子の他に「redo」演算子が用意されています。「redo」演算子はその時の繰り返しを先頭の位置まで戻し改めて繰り返し処理を行います。書式は次の通りです。 redo;例えば次の例で考えてみます。 my $count = 0; while ($count <= 5){ #redo演算子が実行されるとこの位置に進む $count++; if ($count == 2){ redo; } print "$count\n"; } print "終了\n";上記では繰り返し処理の中で条件判断を行い、変数「$count」が「2」と等しい時にはredo演算子が実行されます。redo演算子が実行されると繰り返し処理の最初の位置まで戻り改めて繰り返し処理が行われます。この時、次の繰り返しに進むのではなく同じ繰り返しを改めて開始することになりますので繰り返しを続行するかどうかの条件判断は行いません。 redo演算子の場合も対象となる繰り返し処理というのは、while文、until文、for文、foreach文の他に裸のブロックが対象となります。 では簡単なプログラムで確認して見ます。 test9-1.pl サンプルプログラム下記のサンプルを実行してみよう。# redo演算子 # use strict; use warnings; use utf8; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; my $count = 0; while ($count <= 5){ #redo演算子が実行されるとこの位置に進む $count++; if ($count == 2){ redo; } print "$count\n"; } print "終了\n";上記を「test9-1.pl」の名前で保存してから次のように実行して下さい。 ![]() 今回の例では変数の値が「2」の時に改めて繰り返し処理の先頭に戻ります(先頭に戻って改めて変数に値に1が加算されます)。 Linux 環境での実行結果は、下記です。シフトJIS で出力されるので nkf -w で UTF-8 に変換しています。 [xxxxxxxx@dddddddddd For]$ perl test9-1_u.pl | nkf -w 1 3 4 5 6 終了 [xxxxxxxx@dddddddddd For]$ |
「last」演算子、「next」演算子、「redo」演算子を使うことで繰り返し処理の中でより細かい制御が出来るようになります。ただこれらの演算子の対象は、演算子が記述されている最も内側の繰り返し処理です。 繰り返し処理が多重になっている場合などに、最も内側の繰り返し処理ではなく対象となる繰り返し処理を指定することができます。この場合は各演算子の後ろに対象となる繰り返し処理に付けられたラベル名を指定します。 last ラベル; next ラベル; redo ラベル;ラベルにはアルファベット・数字・アンダーバーが使用できます。ただし先頭に数字は使用できません。小文字でも大文字でも構わないのですが、Perlで用意されている関数などと区別するためにラベルには大文字を使うことが推奨されています。 ラベルは予め繰り返し処理で使われるブロックに対して名前を付けるために使用します。ラベルを付けるには各繰り返し処理の前に「ラベル:」と言う形式で指定します。例えばwhile文の場合は次のような形式となります。 ラベル: while (条件式){ # 何らかの処理 }このように繰り返し処理のブロックに対して名前を付け、その名前を指定してlast演算子やnext演算子を使用します。例えば次のような使い方となります。 CALC: for (my $i = 1; $i < 10; $i++) { for (my $j = 1; $j < 10; $j++){ my $calc = $i * $j; print "$i * $j = $calc\n"; if ($calc > 10){ last CALC; } } }上記の場合、for文が多重となっています。内側の繰り返しの中で掛け算をした結果が10よりも大きくなった場合はlast演算子を使って繰り返し処理を終了させています。通常であればlast演算子が含まれる最も内側の繰り返し処理を終了させるだけですが、今回は場合はラベルを指定してlast演算子を実行していますので、外側の繰り返し処理を対象としてlast演算子が実行されます。結果として一番外側の繰り返し処理が終了します。 このようにラベルを使うことでより細かい制御が行えるようになりますが、あまり多用すると複雑で分かりにくいプログラムとなってしまいますので注意して下さい。 では簡単なプログラムで確認して見ます。 test10-1.pl サンプルプログラム下記のサンプルを実行してみよう。# 繰り返し処理にラベルを付ける # use strict; use warnings; use utf8; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; CALC: for (my $i = 1; $i < 10; $i++){ for (my $j = 1; $j < 10; $j++){ my $calc = $i * $j; print "$i * $j = $calc\n"; last CALC if $calc > 10; } } print "終了\n";上記を「test10-1.pl」の名前で保存してから次のように実行して下さい。 ![]() 今回の例では2つの繰り返しを使って掛け算の結果を表示させていますが、掛け算の結果が10よりも大きくなった時点で、繰り返し全体を終了させます。 Linux 環境での実行結果は、下記です。シフトJIS で出力されるので nkf -w で UTF-8 に変換しています。 [xxxxxxxx@dddddddddd For]$ perl test10-1_u.pl | nkf -w 1 * 1 = 1 1 * 2 = 2 1 * 3 = 3 1 * 4 = 4 1 * 5 = 5 1 * 6 = 6 1 * 7 = 7 1 * 8 = 8 1 * 9 = 9 2 * 1 = 2 2 * 2 = 4 2 * 3 = 6 2 * 4 = 8 2 * 5 = 10 2 * 6 = 12 終了 [xxxxxxxx@dddddddddd For]$ |
繰り返し処理においても実行する処理が1つの場合は式修飾子が使用できます。利用できるのはwhile修飾子、until修飾子、そしてforeach修飾子です。書式は次のようになります。 真の時に実行する処理 while 条件式; 真の時に実行する処理 until 条件式; 実行する処理 foreach 条件式;なおforeach文の場合は制御変数とリストを指定して利用しますが、foreach修飾子では制御変数を指定できませんので省略した場合に使用される「$_」が制御変数として使用されます。 foreach修飾子を使った例としては次のようになります。 my @fruit = ("りんご", "メロン", "イチゴ"); print "$_\n" foreach @fruit;これはforeach文を使って次のように記述した場合と同じです。 my @fruit = ("りんご", "メロン", "イチゴ"); foreach (@fruit){ print "$_\n"; }foreach修飾子はforeach文を使っても記述できますが、1つの処理だけを行う場合には便利な記述方法です。また逆にforeach修飾子では条件式が真の時に実行できる処理は1つだけです。while修飾子やuntil修飾子の場合も同様です。 では簡単なプログラムで確認して見ます。 test11-1.pl サンプルプログラム下記のサンプルを実行してみよう。# 式修飾子 # use strict; use warnings; use utf8; binmode STDIN, ':encoding(cp932)'; binmode STDOUT, ':encoding(cp932)'; binmode STDERR, ':encoding(cp932)'; my @fruit = ("りんご", "メロン", "イチゴ"); print "$_\n" foreach @fruit;上記を「test11-1.pl」の名前で保存してから次のように実行して下さい。 ![]() Linux 環境での実行結果は、下記です。シフトJIS で出力されるので nkf -w で UTF-8 に変換しています。 [xxxxxxxx@dddddddddd For]$ perl test11-1_u.pl | nkf -w りんご メロン イチゴ [xxxxxxxx@dddddddddd For]$ |
|