if を用いて、条件分岐を記述することができます。 パターンはいくつかあります。
- if ( C1 ) { X }
- C1 が真であれば X を実行し、 C1 が偽であれば 何も実行しません。
- if ( C1 ) { X } else { Y }
- C1 が真であれば X を実行し、 C1 が偽であれば Y を実行します。
- if ( C1 ) { X } elsif ( C2 ) { Y } else { Z }
- C1 が真であれば X を実行します。 C1 が偽のときは C2 の真偽を調べて、 C2 が真であれば Y を実行し、 C2 が偽であれば Z を実行します。
例えば、前回の課題の交代級数を第20000項まで和をとることにして、次のようなスクリプトを書くと、 数値の変化はよくわかりますが、大変な時間がかかってしまいます。 また、この出力をファイルに入れた場合、ファイルサイズが巨大になります。 うっかり作ってしまったら、すぐ削除して下さい。
1: $sum = 0; 2: $n = 1; 3: while ( $n <= 200000 ) { 4: $sum = $sum + (-1)**($n+1) * (1/$n); 5: print $sum, "\n"; 6: $n++; 7: }
そこで、最後の2項を足したときのみ和を出力するようにすると、ずいぶん早くなります。
1: $sum = 0; 2: $n = 1; 3: while ( $n <= 200000 ) { 4: $sum = $sum + (-1)**($n+1) * (1/$n); 5: if ($n >199998) { print $sum, "\n"; } 6: $n++; 7: }
この級数が交代級数であることを考えると、極限値はこのふたつの数の中間にあることになり、 小数第5位までが確定します:0.69314。
I:\nyuumon2>jperl nm013.pl 0.693149680566233 0.693144680566233 I:\nyuumon2>
さて、今回はこの if を用いて、与えた3つの数を小さい順にならべて表示するスクリプトを作ってみましょう。
まず最初は、単に与えた数をそのまま画面に出力するスクリプトを書いてみます: nm201.pl
1: if ( @ARGV != 3 ) { die "usage: jperl nm201.pl number1 number2 number3\n"; } 2: ($num1, $num2, $num3) = @ARGV; 3: 4: print "$num1 $num2 $num3\n";
まずこれを素直に実行してみてください:
I:\nyuumon2>jperl nm201.pl usage: jperl nm201.pl number1 number2 number3 I:\nyuumon2>このスクリプトは3つの数字をコマンドライン引数として必要とするので、 なにもつけずに実行した場合などにエラーメッセージ(使用法) を表示して修了するようにしてあります。
簡単に1行目を解説します。
if ( @ARGV != 3 ) { die "usage: jperl nm201.pl number1 number2 number3\n"; }
if のかっこの中は、実行時のコマンドライン引数の数が 3とは異なるとき真になります。真の場合(個数が3でない場合)は続くブロック ({}で囲まれた部分)が実行されます。 die "〜" とは指定した文字列を画面に出力して スクリプトの実行を終了する命令です。 もし真でない場合(個数が3に等しい場合)は、単に次の行に進むことになります。
それでは、今度は任意の3つの数を引数をつけて実行してみてください。
I:\nyuumon2>jperl nm201.pl 31 24 15 31 24 15 I:\nyuumon2>指定した3つの数がその順で表示されました。わざと31と24の間はスペースキーを 2回たたいてみても、表示は等間隔になっています。
それでは2行目を解説します。
($num1, $num2, $num3) = @ARGV;
@ARGV というのは1行目でも出てきました。 Perlの入門(2) で説明しましたが、@で始まる変数には「リスト」が格納されています。 @ARGV には 実行した際のコマンドライン引数がリストとなって格納されています。 連続する1個以上のスペースで区切られてリストになっています。 この行では、そのリストの要素を3つの変数 $num1, $num2, $num3 に代入しています。
3行目は見やすくするために、わざと1行あけています。
4行目で $num1, $num2, $num3 の値を画面に出力しています。
次に、最初のふたつの数字のみに注目して、もし $num1 > $num2 ならば これらを入れ替えてみましょう。もし上の3行目のところに
if ( $num1 > $num2 ) { $num1 = $num2; $num2 = $num1; }
としてやればどうでしょう。うまくいくでしょうか? 残念ながらこれはうまくいきません。 例えば上の例のように $num1 が 31 で $num2 が 24 の場合を考えてみます。 $num1 > $num2 は真ですから、 続くブロックが実行されます。 最初に$num1 に$num2 の値、 つまり24 、が代入されます。 次に$num2 に $num1 の現在の値、つまり24、が代入されます。 つまり31 という数はどこかへ消えてしまい、どちらも24 になってしまいます。
$num1 … 31
$num2 … 24
⇒
$num1 = $num2;
$num1 … 24
$num2 … 24
⇒
$num2 = $num1;
$num1 … 24
$num2 … 24
これは次のように、変数をひとつ追加してやることにより、解決できます:
$num1 … 31
$num2 … 24
$temp … ??
⇒
$temp = $num1;
$num1 … 31
$num2 … 24
$temp … 31
⇒
$num1 = $num2;
$num1 … 24
$num2 … 24
$temp … 31
⇒
$num2 = $temp;
$num1 … 24
$num2 … 31
$temp … 31
課題
- 3つ指定した数のうち最初のふたつを小さい順に並べ替えて表示するスクリプト (nm202.pl)をつくり、自分のページに掲載しなさい。
- 3つ指定した数を小さい順に並べ替えて表示するスクリプト (nm203.pl)をつくり、自分のページに掲載しなさい。
*実は Perlにはリストの要素を大きさの順に並べ替えてくれる関数 sort がありますから、 手作業でこのようなことをする必要はないのです……(^^;
if と逆の働きをする unless というものもあります:
- unless ( C ) { X }
- 条件 C が偽であるとき X が実行され、真であるときは何も実行されません
例えば一番上の例のif ( @ARGV != 3 ) { die "usage: jperl nm201.pl number1 number2 number3\n"; }
は、unless ( @ARGV == 3 ) { die "usage: jperl nm201.pl number1 number2 number3\n"; }
と書くことができます。 さらに、これらは次のように書くこともできます:die "usage: jperl nm201.pl number1 number2 number3\n" if ( @ARGV != 3 );
die "usage: jperl nm201.pl number1 number2 number3\n" unless ( @ARGV == 3 );