Study of CGI

処理の制御

制御構文

制御構文は、条件による分岐処理、繰り返し処理などプログラムを制御するものです。

ある条件が与えることにより、処理を分岐したり繰り返繰り返すといったことをします。

代表的な制御構文は以下のようなものです。

制御文 内容
if 指定された条件によって分岐する。条件分岐。
unless if構文と同様に条件分岐を行うが、条件の真偽が逆になる。
while 指定された条件によって繰り返しを行う。ループ。
until while構文と同様に繰り返しをを行うが、条件の真偽が逆になる。
do while/until while/untilをが、必ず1回は処理される形のもの。
for 指定した条件によってを繰り返し処理を行う。
foreach for構文と同じだが、記述方法が異なる。

if文と unless文

if文と unless文は、与えられた条件によって処理を分ける制御構文です。

if文は、「もし、〜ならば、〜をする」というような処理を行うための仕組みです。unless文は逆に「もし、〜でなければ、〜をする」となります。

まずif文ですが、制御式(control expresstion:真偽の判定のために評価される式)と1個のブロックから構成されます。「{」と「}」で囲まれた範囲を、文ブロックといいます。また、必要に応じて else ともう一個のブロックを持つことができ、以下のような形になります。

if ( one_expression ) {
    true_statement_1 ;
    true_statement_2 ;
    true_statement_3 ;
} else {
    false_ statement_1 ;
    false_ statement_2 ;
    false_ statement_3 ;
}

制御式の真偽の定義ですが。Perl では、制御式をスカラーコンテキストで評価(値が数値であれば文字列に変換)します。得られた文字列が空文字列(長さ0の文字列)か、ひとつだけの文字 "0"(数字のゼロ)であれば偽を返して、それ以外は真となります。

実際の解釈の例を下に示します。

0
1 - 1
1
" "
"00"
"0.00"
undef
文字 "0" に変換されるので、偽
答えのゼロが "0" に変換されて、偽
"1" に変換されて、真
空文字列は、偽
" " でも "0" でもないので、真 (これは要注意)
上と同じ理由で、真
評価すると " "(空文字)となるので、偽

また、制御式にはスカラー変数、一般的な式、比較式などが入ります。ある程度複雑な条件を与える場合には、比較式が多く使われます。

unless文はif文同様、制御式(control expresstion:真偽の判定のために評価される式)と1個のブロックから構成され、必要に応じて else ともう一個のブロックを持つことができます。

ただ、構成は同じですが、結果は逆で「もし制御式が偽なら、〜せよ」となります。

elsif

if文も unless文も elsif を追加することにより条件式を追加していくことができます。

if ( expression_one ) {
    one_true_statement_1 ;
    one_true_statement_2 ;
} elsif ( expression_two ) {
    two_true_statement_1 ;
    two_true_statement_2 ;
} elsif ( expression_three ) {
    three_true_statement_1 ;
    three_true_statement_2 ;
} else {
    all_false_ statement_1 ;
    all_false_ statement_2 ;
}

各制御式は順番に計算され、どれかの制御式で真がでればその対応文ブロックが実行され、それ以降の制御式とそれに対応する文ブロックはスキップされます。

最終的にすべての制御式が偽でelseの分岐が存在すれば、elseの分岐が実行されます。

if分岐とelse分岐は前後に一つ以上存在してはいけませんが、elsif分岐はいくつあってもかまいません。

while文とuntil文

while文は制御の繰り返し(ループ処理)を行います。

while文は、まず制御式を評価しその結果が真なら while文の本体を実行します。

実行後は再び文ブロックの最初に戻り実行を行いますが、その前に制御式の再評価を行います。

このとき、本体の実行により制御式の結果が変わっており、かつその結果が偽の場合は本体の実行は行わず、本文の次の行に移ります。

while文は、制御式の結果が偽になるまで、文の本体を繰り返します。(つまり、偽にならなければ永遠に繰り返すことになってしまいます)

 while ( some_expression ) {
   statement_1 ;
   statement_2 ;
   statement_3 ;
  }

それに対し、until文は、まず制御式を評価しその結果が偽なら、while文の本体をします。

実行後は再び文ブロックの最初に戻り実行しますが、そのときに制御式の再評価がなされ、真ならば、ループを抜けます。

文の形は、while文とほぼ同じでwhileがuntilになるだけです。

while文、until文ともに、制御式が最初から終了条件(while文は真、until文は偽)になると本体の文は一度も実行されません。逆に、終了条件にならないプログラムは永遠に繰り返しサーバーエラー等の引き金になるので注意が必要です。

do { } while / until文

while文・until文は本文の実行前に条件式の判定を行いますが、この構文は本文を実行した後に制御式の評価をします。本文の実行前に、判定が行われるので必ず一度は実行がされます。

do { } while文

制御式の評価が偽ならループは終了しますが、真なら繰り返しループして制御式が偽になるまで繰り返します。本文の実行後に、判定が行われる以外は while文と同様です。

 do {
   statement_1 ;
   statement_2 ;
  } while some_expression ;

do { } until文

同様に、do { } until文もループで本文を実行した後に制御式の評価をします。

評価が真ならループは終了しますが、偽なら繰り返しループして制御式が真になるまで繰り返します。

for文

for文はwhile文と同様の繰り返し構文ですが、条件式に、3つの式を順番に与えます。

初期化式、制御式、再初期化という並びをセミコロンを使って区切り条件式としてあたえます。

 for ( initial_exp; test_exp; re-init_exp ) {
   statement_1 ;
   statement_2 ;
}

まず、ループに入る前に一度だけ初期化式initial_expが実行されます。

initial_expでは、一般的にループ変数に初期値を設定します。しかし、式の内容には何の制限も無く、空でもかまいません。

次に、真偽を評価する制御式test_expが実行されます。評価の結果、値が真なら本文を実行させ、偽ならループは終了します。

本文が終了すると、再初期化式re-init_expが実行されます。一般には、ここで評価する値を増減させます。そして、ここで増減させた値を持って、再び制御式を評価し、真なら本文を実行、偽ならループを終了します。

次の例では数値の1から10までを、後ろにスペースを付けて表示し改行します。

for ( $i = 1; $i <= 10; $i ++ ) {
   print "$i \n" ;
}

まず、$i に初期値の1をセットします。

次にその数値を制御式にて評価します。1 =< 10 なので評価は真となり本文 print が実行されます。

次に再初期化式を実行します。すると、変$iはオートインクリメント演算子にて2になります。

そして、再び 制御式が評価され、まだ10以下なので本文が実行されます。

こうして、制御式で偽が出るまで、つまり変数$iが11になるまで繰り返されます。

foreach文

foreach文は、値のリストを受け取りそれらを1つずつ順にスカラー変数に代入して、コードのブロックを実行するループ文です。

 foreach $i ( @some_list ) {
   statement_1 ;
   statement_2 ;
   statement_3 ;
}

このスカラー変数はループを抜けると自動的に元の値に戻されます。

 @a = ( 1 , 2 , 3 , 4 , 5 ) ;
  foreach $i ( reverse @a ) {
   print $i ;
  }

これを実行すると 54321 と表示されます。

与える リスト値は、配列変数に限らず任意のリスト式でもよいです。(これは、リストを要求する全てのケースあてはまります)

また、スカラー値の$iを省略することもできます。

 @i = ( 1 , 2 , 3 , 4 , 5 ) ;
  foreach ( reverse @i ) {
   print ;
  }

Perlの操作の多くは、デフォルトで$_に対して作用します。

上の例では、foreach文に与えるスカラー値が省略されているので、$_が与えられたと解釈され、さらにprintでもカラー値が省略されているので、ここでも$_が与えられたと解釈されます。

結果として、上の二つの例はまったく同じ動作をします。

このように、スカラー変数を省略すると Perl の操作の多くは $_ を指定したものとして作動するので、$_ は作業用変数として扱うことができます。

繰り返しの対象になるリストが関数ではなく本物の変数である場合は、ループ変数は単なるコピーではなく、実際のリストの各変数へのエイリアスとして扱われます。つまり、ループ変数の値を変更したらその変数が表しているリスト要素も同時に変更されます。

 @a = ( 1 , 2 , 3 ) ;
  foreach $one (@a ) {
   $one *= 3 ;
  }

ループの中で、ループ変数$oneはそれぞれリスト値の1、2、3を指し示すます。ですから、おのおのリスト値に対し、本文の操作(二項代入演算子で要素に3をかける)を行ったのと同じ状態になり、配列@a は ( 3 , 6 , 9 ) になります。

ループの制御

perlにはループの流れを変えるための仕組みがいくつか用意されています。それが、last文、next文、redo文です。(ただし、do { } while / until文には作用しません)

通常のループ処理の中で特定の条件が発生したときに、処理を行うように通常はif文などと組み合わせて使われます。

last文

last文は、ループを構成するブロック(for、foreach、while、untilおよび空の文ブロック)の最も内側のブロックから抜け出し(そのブロックを終了し)、ブロック直後の文から実行を継続します。

 while ( something ) {
   befor_statement_1 ;
   befor_statement_2 ;
   if ( somecondition){
      somethingorother;
      somethingorother;
      last;# ループを抜ける
   }
   after_statement_1 ;
   after_statement_2 ;
  }
  # ループを抜けてここに来る

たとえば、あるディレクトリのテキスト文書すべて開いて特定の語を探すとき。その後さえ見つかれば残りの処理は不要です。しかし、見つからなかった場合はテキスト文書をすべて検査します。

next文

next文は、最も内側のループの残りのブロックをスキップします。

 while ( something ) {
   befor_statement_1 ;
   befor_statement_2 ;
   if ( somecondition){
      somethingorother;
      somethingorother;
      next;# ブロックを抜ける
   }
   after_statement_1 ;
   after_statement_2 ;
  # nextによりここに来る
  }

上の例では、someconditionが真となったとき、after_statement_1、2は実行されません。

redo文

redo文は現在のブロックの先頭にジャンプします。つまり、現在のブロックをやり直します(制御式の再評価はしません)。

 while ( something ) {
  # redoによりここに来る
   befor_statement_1 ;
   befor_statement_2 ;
   if ( somecondition){
      somethingorother;
      somethingorother;
      redo;# ブロックをやり直す
   }
   after_statement_1 ;
   after_statement_2 ;
  }

ブロックのラベル

2つ以上の入れ子になった(ネストされた)ループの文ブロックから一度に抜け出す場合などには、ブロックのラベルを使用します。

ブロックにラベルを使用し名前を付けておけば、任意のブロックに対し last、next、redo、を適応させることができます。

ラベル名はスカラー変数、配列変数、ハッシュ変数、サブルーチンと同様の規則に従った名前を付けます。

これらは、それ独自の名前空間で管理されており同じ名前を付けることができます。しかし、変数のように先頭に特別な文字を付けないので、予約語と衝突する恐れがあります。

そのため、予約語と重ならないようラベルは大文字と数字のみからなる名前を付けることが推奨されています。(大文字と数字のみからなる名前は将来的にも予約語となることはありません)

OUTER : for ( $i = 1 ; $i <= 10 ; $i++) {
      INNER : for ( $j = 1 ; $j <= 10 ; $j++) {
           if ( $i * $j == 63) {
                print "$i times $j is 63!\n";
                last OUTER ;
           }
           if ( $j >= $i) {
                next OUTER ;
           }
      }
}

lastやnextにラベル名の「OUTER」が追加されています。

これにより、last文やnext文が作用するのが、一番内側のループ(INNER)ではなく、外側のOUTERであることがわかります。ラベル名が指定されない場合はこれまでどおり、一番内側のループが対象となります。

また、ラベルを使って外に出ることはできますが、内側のループに入ることはできません。

式修飾子

修飾子(modifier)を使うと、「もし〜なら、〜をしなさい」の表現するのに、文ブロックを使わずに単体で文を組み立てることができます。

some_expression if control_expression;

まず、control_expressionを評価し、もしそれが真であればsome_expressionを実行します。

これは、次のブロックを使ったif文とほぼ等価です。

if ( control_expression ){
     some_expression ;
}

式修飾子を使用した場合は、文ブロックは使えませんので、文自体が簡素なものでなければなりません。

同様の方法で、unless、while、until がを使用することができます。

また、「&&」 と 「||」 といった論理演算子を使うことでも、「もし〜なら、〜をしなさい」の条件式をつくることができます。

「&&」 は左辺が真である場合、右辺を評価(実行)します。逆に偽であれば評価(実行)しません。

同様に、「||」 は左辺が偽である場合、右辺を評価(実行)し、真であれば評価(実行)しません。

つまり、「&&」 はif文、 「||」はunless文と等価になります。

ポイント

  1. 制御式は結果が空文字列、又は"0"(であれば偽、それ以外は真。
  2. ループ文は、かならずループから抜ける方法を用意しておく。
  3. Perlの多くは、スカラー変数を省略するとデフォルトで$_に対して作用する。
  4. ループ文の流れを変える場合はlast・next・redo文を使用する。
  5. last・next・redo文は、do { } while / until文には作用しない。
Operator Study of CGI Sub

□新着

  • 2016/04/14
    ページ復旧
  • 2007/10/06
    Story Maker始動
  • 2007/06/13
    久々の更新
  • 2007/01/06
    logちょっとバージョンアップ
  • 2006/07/27
    ホームページのPerl 終了

□Topics

  • 2016/04/14
    サイト復旧
  • 2007/10/06
    StoryMaker始動
  • 2007/06/13
    今後にぜひご期待を!
  • 2006/06/26
    今後について
  • 2006/06/15
    環境の変化