ループは、もちろん、特定区間の実行を何度か繰り返す機構であるが、確定ループ(Definite Loop)とは、その繰り返し回数の限界が、実行開始前に定まっているループである。

DO LOOP


Forthの最も基本的な確定ループはDO-LOOPである。このループの巡回区間は、DOで始まりLOOPで終わる部分である。DO-LOOPは、forthにおける他のコントロールフロー制御ワードと同様、ワード定義内でしか利用できない。

DOは実行時にスタックから2つのアイテムを入力として取る。1つは、巡回カウンターの境界値であり、もう1つは巡回カウンターの初期値である。
DO  ( n1 n2 -- )   \ n1が境界値、n2がカウンターの初期値。
カウンターはLOOPに至ったときに1加算される。そして、カウンターが境界値に一致するかそれより大きくなったときにループを抜ける。

I

また、カウンターの数値は、特別なワードであるI によってスタック上に取り出すことができる。
例えば、
: 10-count  10 0 DO I . cr LOOP  ;
10-count
とすれば、0から9までの10個の数字が印字される。つまり、カウンターが増えていく方向では、境界値と一致した場合にはループは実行されない。初期値、境界値とも、負の数でも良い。またスタック上の値から取られるのであるから、固定した数字である必要はなく、変数でも、計算した結果でもかまわない。

しかし、通常のDO LOOPの場合、境界値が初期値より小さい場合でも、必ず一回はループ内が実行されてしまう。

?DO

そこで、境界値が初期値以下の場合にはループを実行しないようにするためには、初めにDOではなくて?DOを使う。基本はDOと同じである。
?DO ( n1 n2 -- )
Forth標準としての定義は、n1=n2のときにはループを実行しないが、それ以外のときはループに入る、というものである。この規定によれば、境界値が初めから初期値より小さいときには、1回ループ内を実行するということになる。
iMopsでは、もう少し複雑に対応しており、下で述べるインターバル値が定数として確定している場合、指標値が既に壁を越えているとみなされれば、
ループ内を実行しないようになっている。

+LOOP

LOOPではカウンターは1ずつ加算されていくしかないが、代わりに+LOOPを用いればループカウンターのステップを操作することができる。このステップの値は、インターバル値ともいわれる。+LOOPは、スタックの値を1つとって、それをカウンターへの加算値とする。負の数を用いれば、カウンターは減少していくわけである。
+LOOP  ( n -- )  \ ループカウンターにnを加算して折り返し
したがって、I をカウンター呼ぶのは、この場合には相応しくない。実際、Iはインデクス(index:指標)という意味であり、カウンター値というより、指標値というべきなのである。しかし、指標値では今度は抽象的過ぎて何を指しているのかわかりにくい。ともかく、ループの巡回を続けるか否かを決めるための判定値なのであって、それが初めに与えられたもう1つの値である境界値と比較されて巡回を続けるかどうかが決められるのである。

しかし、+LOOPを用いる場合、境界値との比較に関して注意すべきことがある。例えば、
: -10-count 0 10 DO i . cr -1 +LOOP  ;
-10-count
では、10から0までの11個の数字が印字される。つまり、指標値が境界値と一致したときにもループ内が実行される。インターバルが正の場合と負の場合とで、結果が対称ではないのである。
これについて標準的な考え方として示されているのは、
“境界値と(境界値-1)の中間に壁があり、指標値がその壁を交差したときにループから脱出する”
というものである。この考えによれば、上のDO-LOOPの指標が増加していく例では、境界値10と9との中間、9.5に壁があるので、指標値は9まではくり返すが、10になったら壁を交差したことになり、ループから直ちに抜けることになる。後の、指標が減少していく例では、境界値の0と-1の中間、-0.5に壁があり、指標値は0までは壁を越えていないのでループが実行されるが、-1になったとき初めて壁を越えて、ループが終了する、というわけである。
少し考えてみると、これは、インターバルが正か負かで、判定条件が論理的に反転しているだけであることに気づく。つまり、指標値が増えていくときには、
「指標値が境界値より小さい」を条件にループを繰り返し、指標値が減少していくときは、「指標値が境界値より小さい」を条件に繰り返しをやめる、
という関係になっているのである。

インターバル値はループが一巡する毎にスタックに置かれなければならないが、スタック値であればなんでもよく、したがって、計算した値でもよい。したがって、インターバル値が各回変動しても良い。そうなると、もはや確定ループとはいえない状況になるが。

入れ子ループ指標 -- I J K

DO-ループの中にDO-ループを置くことももちろんできる。DOとLOOPがある種、括弧構造を成す。その場合、指標値 I は、その場所を囲う最も内側のループの指標値である。これに対し、2つの入れ子のループの内側で、外側のループの現在の指標値をスタックに複製するワードがJである。例えば、
: double-count   3 0 DO 0 -3 DO i . j . cr  LOOP LOOP  ;
double-count
とすると、
-3 0 
-2 0 
-1 0 
-3 1 
-2 1 
-1 1 
-3 2 
-2 2 
-1 2 
のように印字される。負の数が内側ループの指標iに当たり、0以上の方が外側ループの指標jに当たる。

3つのDO-ループの入れ子で、一番内側のループ内で一番外側のループの指標値をとるKというワードが定義されている環境もあるが、これはforth標準ではないので定義されていない場合もあり得る。

なお、ループの入れ子とは、1つのワード定義内に多重ループを書き込んでいる場合のことであって、あるワードのループ内から、ループを含む別のワードを呼び出すときは、これに該当せず、Jを用いることはできないので注意。

LEAVE

LEAVEは、特別な状況が発生したときに、ループを途中で抜けるためのワードである。通常は、IF文とともに利用する。LEAVEが抜けるのは、それが含まれている1つのDO-ループだけである。脱出した後は、LOOPより後が実行される。例えば、
: At-Most-5 1+ 1 DO  i dup . 5 > IF  ." over the limit! " cr LEAVE THEN cr LOOP ." end." cr ;
10 At-Most-5 
1 
2 
3 
4 
5 
6 over the limit! 
end.
というように、end.はLEAVEが実行されてもされなくても、印字される。

UNLOOP

上のLEAVEはループを抜けるだけであるが、そのループを含むワードそのものを脱出したい場合もある。そのときにはEXITを用いるのであるが、ループの中では、リターンスタックなどにパラメターが配備されているため、それを整理してからでなければワードから抜け出ることができない。その整理をするのがUNLOOPである。例えば上と似ているが、
: At-Most-5x 1+ 1 DO  i dup . 5 > IF  ." over the limit! " cr UNLOOP EXIT THEN cr LOOP ." end." cr ;
のように定義すると、入力が5を越えている場合には、最後にend.が印字されない。
UNLOOPが整理するのは、DO-ループによって配備された部分だけである。後で述べるような、プログラマーがリターンスタックに自分でデータを
積み込んだ場合には、予め取り出しておかないとUNLOOPでは整理できず、フローが混乱し、大抵はクラッシュする(エクセプション)ので注意を要する。
なお、forth標準では、DO-ループを入れ子にしている場所からEXITするには、ループの個数分だけUNLOOPを呼び出されければならない。

FOR NEXT


Mopsの拡張として、簡便な確定ループであるFOR NEXTが定義されている。このループは、もともとはforthの発明者であるCharles Mooreが導入したものであるとされるが、forth標準にはない。FOR NEXTループを拡張として持つforth環境は多いようであるが、環境毎に仕様が微妙に食い違うため、注意しなければならないといわれる。ここでは、Mopsでの動作について説明する。

FORは、スタックから1つ値を取って、それを繰り返し回数とする。繰り返し実行される範囲は次のNEXTまでである。0以下であればループ内は実行されない。
FOR   ( u -- ) \ スタック上の値をセットし、NEXTまでの間をu回繰り返す。
NEXT  ( -- ) \ FORまで実行個所を返す
指標値 I が利用でき、入力値にnを渡せば、指標値は(n-1)から始まって、1ずつ減少し、0まで繰り返される。

FOR-ループの中では、DO-ループの場合と同じように指標値 J , Kが利用できる。DO-ループと入れ子にもできる。

iMopsではFOR NEXTからもLEAVEを用いることができる。
また、Mops全般に、FOR NEXT内からEXITするには、UNLOOPではなくUNFORを用いる。

次は、



最終更新:2014年10月15日 17:50