これまでforeachはやってきたけど、その他にもいくつか繰り返しのための構文がある。
foreach
リストに対して次のような形で使える。
foreach (リスト) { # 要素へのアクセスは $_ でできる }
じつはforと全く同じ(後述)。
while
おなじみwhileループもある。次のスクリプトは、file.txtというファイルの中身を1行ずつ取り出して出力する。
open(FILE, 'file.txt') or die "$!"; while (<FILE>) { print $_; } close(FILE);
上記のスクリプトでは、1行ずつ取り出してはデフォルト変数 $_ に代入されるらしい。ここでは「ファイルの中身を1行ずつ取り出す」という例だったので、デフォルト変数を使うケースとして妥当なのはわかる。だけど、whileは「ある条件を満たす間繰り返す」という一般的な制御構造なので、例えば「フラグ $flag が真の間繰り返す」みたいな場合には、デフォルト変数には一体何が格納されているのだろう。
気になったのでやってみた。http://ideone.com/rbkSj
my @week = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'); my $flag = 1; while (defined($flag)) { $flag = pop(@week); print "\$flag = $flag, \$_ = $_\n"; }
undefが格納されているっぽい?デフォルト変数の束縛のルールがよく分からん。
ループの制御として、
- last
- next
- redo
というのがあるらしい。
last
C言語などにおけるbreakみたいなものっぽい。
my $n = -1; while ($n < 9) { $n++; print $n, ':'; my $m = -1; while ($m < 9) { $m++; last if ($m >= 5); print $m, ' '; } print "\n"; }
内側のループが中断される。http://ideone.com/zYHep
next
C言語などにおけるcontinueみたいなものっぽい。(あれ?Cにcontinueとかあったっけ?まあいいや。)
my $n = -1; while ($n < 9) { $n++; print $n, ':'; my $m = -1; while (print("check$m "), $m < 9) { $m++; next if ($m == 5); print $m, ' '; } print "\n"; }
ループを次のステップへ進めるための構文ぽいね。http://ideone.com/nisis
次のredoと比較するために、内側のwhileにprint入れてみた。
redo
nextと同様、ループの先頭に戻るけど、条件のチェックをしないらしい。
my $n = -1; while ($n < 9) { $n++; print $n, ':'; my $m = -1; while (print("check$m "), $m < 9) { $m++; redo if ($m == 5); print $m, ' '; } print "\n"; }
上記のスクリプトを実行すると、check5が出なくなる。http://ideone.com/HOhZA
nextと似てるけど違う。使いどころがよく分からん。
for
これもおなじみ。
for (my $i = 0; $i < $n; $i++) { }
なんと、Perlではforeachとforは全く同じらしい。以下のスクリプトは全部同じ挙動をする。
for (my $i = 0; $i < 5; $i++) { print $i, ' '; }
foreach (my $i = 0; $i < 5; $i++) { print $i, ' '; }
for (0, 1, 2, 3, 4) { print $_, ' '; }
foreach (0, 1, 2, 3, 4) { print $_, ' '; }
最後の2つはリストを渡してるけど、リストを生成してくれる..演算子を使うともっと簡潔に書ける。
for (0 .. 4) { print $_, ' '; }
こんな感じ。もちろん、これもforeachでも構わない。
forやforeachにリストを与えると、デフォルト変数 $_ を束縛してくれるけど、for (my $i = 0; $i < 5; $i++) のタイプで書いたら、一体何がデフォルト変数を束縛するのだろう。...ってやっぱりundefのような気がするけどやってみた。http://ideone.com/L3ILn
for (my $i = 0; $i < 5; $i++) { print "\$i = $i, \$_ = $_\n"; }
やはり見慣れたエラーメッセージがでた。
Use of uninitialized value in concatenation (.) or string
きっとundefなんだろうねー。
last、next、redoはwhileと同様に使えるっぽい
whileのところでやったlastとnextとredoをforでも使ってみた。$iや$jのインクリメントのタイミングとprintのタイミングが違うので、whileの時と同じ結果にはならないけど。
for (my $i = 0; $i < 10; $i++) { print $i, ':'; for (my $j = 0; $j < 10; $j++) { last if ($j >= 5); print $j, ' '; } print "\n"; }
for (my $i = 0; $i < 10; $i++) { print $i, ':'; for (my $j = 0; print("check$j "), $j < 10; $j++) { next if ($j == 5); print $j, ' '; } print "\n"; }
for (my $i = 0; $i < 10; $i++) { print $i, ':'; for (my $j = 0; print("check$j "), $j < 10; $j++) { redo if ($j == 5); print $j, ' '; } print "\n"; }
最後のredoでのスクリプトを実行すると無限ループになった。for (my $i = $start; $i < $end; $i++) の形式の中でredoを使った場合、条件のチェックのみならずインクリメントの部分もスキップされるからではないかな?
...とそれよりも、思いがけない事がおこった。print $i, ' '; の行は実行されても良いと思うけど実行されてないっぽい。少なくとも表示はされない。意味不明。
変数のスコープ
PHP(僕が知ってるのは5.2.5だけだけど)ではfor文やwhile文のbrace内だけがスコープとなるようなローカル変数は作れなかったけど、変数宣言のmyを
for (my $i = 0; $i < 5; $i++) { # do something... } for (my $i = 0; $i < 10; $i++) { # do the another job... }
みたいに書けるという事は、brace単位の細かいローカル変数が作れるっていうことかな?
多重forループで、デフォルト変数 $_ を束縛するのは何者?
やってみた。
for (0 .. 5) { print $_, ' '; for (10 .. 15) { print $_, ' '; } print $_, "\n"; }
お!すごい。ちゃんと意図した通り動いてる。http://ideone.com/ql7CU
そうか、my $_ が見えてないだけだもんなー。ちゃんとローカル変数になってるわけだ。
デフォルト変数 $_ の束縛
直感だけど、ある式がリストコンテキストで評価されるときだけ働くっぽい?