基本のカウント・ループ:
limit start ?DO body LOOP
これは、 start から始まり limit まで(limit 自身は除く)、 各整数値に対して 1 回の反復を実行します。
カウンタ、 つまりインデックスには、 i
を使用してアクセスできます。 たとえば、 以下のループをご覧ください:
10 0 ?DO i . LOOP
出力: 0 1 2 3 4 5 6 7 8 9
最も内側のループのインデックスには i
を使用してアクセスでき、 その一つ外側のループのインデックスには j
を使用してアクセスでき、 さらにもう一つ外側のループのインデックスには k
を使用してアクセスできます。
i'
を使用すると最も内側のループの limit にアクセスでき、 delta-i
を使用すると i'
-
i
にアクセスできます。
: foo 7 5 ?do cr i . i' . delta-i . loop ;
出力:
5 7 2 6 7 1
ループ制御データはリターン・スタックに保持されるため、 リターン・スタックへのアクセスとカウント・ループ・ワードの混在にはいくつかの制限があります。 特に、ループの外側のリターン・スタックに値を置いた場合、 ループ内で値を読み取る事はできません12。 ループ内のリターン・スタックに値を置く場合は、 ループの終了前、 およびループのインデックスにアクセスする前に値を削除する必要があります。
カウント・ループにはいくつかのバリエーションがあります:
LEAVE
は、 最も内側のカウント・ループを直ちに抜け出します。 それが関係する LOOP
または NEXT
の後へ実行は移ります。 例:
10 0 ?DO i DUP . 3 = IF LEAVE THEN LOOP
出力: 0 1 2 3
UNLOOP
は、 例えば EXIT
などを介しての異常なループ終了の準備を行います。 UNLOOP
は、
EXIT
がリターン・アドレスに到達できるように、 リターン・スタックからループ制御パラメーターを削除します(訳注: 1重ループなので
unloop 1つ。2重ループなら unloop 2つ)。 例:
: demo 10 0 ?DO i DUP . 3 = IF UNLOOP EXIT THEN LOOP ." Done" ;
出力: 0 1 2 3
?DO
はループを開始します(ラップアラウンド演算(処理可能な範囲をの最後に達した後に最初に戻る事)によって両者が等しくなるまで LOOP
を反復します)。 通常、 この振る舞いは望ましくないものです。 したがって、 Gforth は (?DO
の代替として、)
+DO
と U+DO
を提供します。 これらは、 start が limit
より大きい場合にはループを開始しません。 +DO
は符号付きループ・パラメータ用で、 U+DO
は符号なしループ・パラメーター用です。
?DO
は DO
に置き換えることはできます。 DO
は、 ループ・パラメーター に関係なく、
常にループに入ります。 あなたが、 どの場合にもループに入ることを知っている場合でも DO
は使用しないでください。
このような知恵はプログラムを保守していく中で無効になる傾向があり、 それゆえ DO
が問題を引き起こすことになります。
LOOP
は n +LOOP
に置き換えることができます。 これにより、インデックスが 1 ではなく n
によって更新されます。 limit-1 と limit の間の境界を越えると、 ループは終了します。 例:
4 0 +DO i . 2 +LOOP
出力: 0 2
4 1 +DO i . 2 +LOOP
出力: 1 3
n +LOOP
の動作は奇妙です:
-1 0 ?DO i . -1 +LOOP
出力: 0 -1
0 0 ?DO i . -1 +LOOP
出力: なし。
私たちは ?DO
と +LOOP
を組み合わせないことをお勧めします。 Gfors はいくつかの代替手段を提供します:
I
=limit の反復を含める -1 +LOOP
の振る舞いが必要な場合、 -[DO
または
U-[DO
] でループを開始します(ここで、 [
は、 包含範囲の数学的表記法(例: [1,n]
からインスピレーションを得ています):
-1 0 -[DO i . -1 +LOOP
出力: 0 -1
.
0 0 -[DO i . -1 +LOOP
出力: 0
0 -1 -[DO i . -1 +LOOP
出力: なし。
limit を除外したい場合、 代わりに 1 -LOOP
(または一般的には u -LOOP
)を使用し、
?DO
または -DO
または U-DO
でループを開始します。 -LOOP
は、limit+1 と limit の間の境界を越えたときにループを終了します。 例:
-2 0 -DO i . 1 -LOOP
出力: 0 -1
-1 0 -DO i . 1 -LOOP
出力: 0
0 0 -DO i . 1 -LOOP
出力: なし。
残念ながら、 +DO
, U+DO
, -DO
, U-DO
, -LOOP
は 標準
Forth では定義されていません。 ただし、 標準のワードのみを使用するこれらのワードの実装が compat/loops.fs
にて提供されています。
bounds
があるため、セル配列 v
を介した前方ループを以下のように記述できます:
create v 1 , 3 , 7 , : foo v 3 cells bounds U+DO i . cell +LOOP ; foo
これは 1 3 7
を出力します。 逆方向にたどるための入力の前処理はより複雑であるため、 Gforth はそれを行う
MEM-DO
… LOOP
形式のループ構造を提供します。 これは addr uバイト
表現の配列と要素サイズを受け取り、 要素のアドレスを逆順に反復処理します。
create v 1 , 3 , 7 , : foo1 v 3 cell array>mem MEM-DO i . LOOP ; foo1
これは 7 3 1
を出力します。ARRAY>MEM
は addr uelems uelemsize を
MEM-DO
が期待する addr ubytes uelemsize に変換します(ubytes は uelems *
uelmsize です)。 このループは MEM-DO
通過後にuelemsize ずつ減算される、 LOOP
と対になるループとなります。
Gforth は、 完全を期すために MEM+DO
も追加します。 MEM-DO
と同一のパラメーターを受け取りますが、
配列を順(forwards)に処理します:
create v 1 , 3 , 7 , : foo2 v 3 cell array>mem MEM+DO i . LOOP ; foo2
出力: 1 3 7
n FOR body NEXT
これは、 ?DO
ループを適切に最適化するのが面倒な、 ネイティブ・コード・コンパイラー作成者達が好むループです。 このループ構造は標準
Forth では定義されていません。 Gforth では、 このループは n+1 回繰り返します。 i
は、 n
で始まり 0 で終わる値を生成します。 他の Forth システムは、FOR
ループをサポートしている場合でも、
振る舞いが異なる場合があります。 この問題を回避するには、 FOR
ループを使用しないようにしてください。
カウント・ループ・ワード群:
?DO
( compilation – do-sys ; run-time w1 w2 – | loop-sys ) core-ext “question-do”
See Counted Loops.
+DO
( compilation – do-sys ; run-time n1 n2 – | loop-sys ) gforth-0.2 “plus-do”
See Counted Loops.
U+DO
( compilation – do-sys ; run-time u1 u2 – | loop-sys ) gforth-0.2 “u-plus-do”
See Counted Loops.
bounds
( addr u – addr+u addr ) gforth-0.2 “bounds”
開始アドレス addr と長さ u で表されるメモリー・ブロックを指定すると、 u+do
または ?do
の終了アドレス addr+u と開始アドレス addr を正しい順序で生成します。
-[do
( compilation – do-sys ; run-time n1 n2 – | loop-sys ) gforth-experimental “minus-bracket-do”
負の方向へカウントされるループを開始します。 n2<n1 の場合、 ループをスキップします。 このようなカウント・ループは、
増分が負である +loop
と対になります。 I
>=n1 である限り実行されます。
u-[do
( compilation – do-sys ; run-time u1 u2 – | loop-sys ) gforth-experimental “u-minus-bracket-do”
負の方向へカウントするループを開始します。 u2<u1 の場合、ループをスキップします。 このようなカウント・ループは、 増分が負の
+loop
と対になります。 I
>=u1 である限り実行されます。
-DO
( compilation – do-sys ; run-time n1 n2 – | loop-sys ) gforth-0.2 “minus-do”
See Counted Loops.
U-DO
( compilation – do-sys ; run-time u1 u2 – | loop-sys ) gforth-0.2 “u-minus-do”
See Counted Loops.
array>mem
( uelements uelemsize – ubytes uelemsize) \ ubytes は
uelements * uelementsize です
mem+do
( compilation – w xt do-sys; run-time addr ubytes +nstride – ) gforth-experimental “mem-plus-do”
I
を addr から開始し、 I
<addr+ubytes である限り、 nstride
幅のステップでメモリー内をアドレスが増える方向にカウント・アップするカウント・ループを開始します。 loop と対にする必要があります。
mem-do
( compilation – w xt do-sys; run-time addr ubytes +nstride – ) gforth-experimental “mem-minus-do”
I
を addr+ubytes-ustride として開始し、 I
>=addr である間
-nstride 幅のステップでメモリーをアドレス下位方向(backward)にステップするカウント・ループを開始します。 loop
と対にしなければなりません。
DO
( compilation – do-sys ; run-time w1 w2 – loop-sys ) core “DO”
See Counted Loops.
FOR
( compilation – do-sys ; run-time u – loop-sys ) gforth-0.2 “FOR”
See Counted Loops.
LOOP
( compilation do-sys – ; run-time loop-sys1 – | loop-sys2 ) core “LOOP”
See Counted Loops.
+LOOP
( compilation do-sys – ; run-time loop-sys1 n – | loop-sys2 ) core “plus-loop”
See Counted Loops.
-LOOP
( compilation do-sys – ; run-time loop-sys1 u – | loop-sys2 ) gforth-0.2 “minus-loop”
See Counted Loops.
NEXT
( compilation do-sys – ; run-time loop-sys1 – | loop-sys2 ) gforth-0.2 “NEXT”
See Counted Loops.
i
( R:n – R:n n ) core “i”
n は、最も内側のカウント・ループのインデックスです。
j
( R:n R:w1 R:w2 – n R:n R:w1 R:w2 ) core “j”
n は、 最も内側から数えて 2 番目のカウント・ループのインデックスです。
k
( R:n R:w1 R:w2 R:w3 R:w4 – n R:n R:w1 R:w2 R:w3 R:w4 ) gforth-0.3 “k”
n は、 最も内側から数えて3番目のカウント・ループのインデックスです。
i'
( R:w R:w2 – R:w R:w2 w ) gforth-0.2 “i-tick”
最も内側のカウント・ループの limit
delta-i
( r:ulimit r:u – r:ulimit r:u u2 ) gforth-1.0 “delta-i”
u2=I'
-I
(limit とインデックスの差)
LEAVE
( compilation – ; run-time loop-sys – ) core “LEAVE” \ 訳注:LEAVE
は、 最も内側のカウンタ付きループを抜け出します。 \ それが関係するLOOP
またはNEXT
の直後から実行を続行します。
See Counted Loops.
?LEAVE
( compilation – ; run-time f | f loop-sys – ) gforth-0.2 “question-leave”
\訳注: f が true なら leave します。
See Counted Loops.
unloop
( R:w1 R:w2 – ) core “unloop”
DONE
( compilation do-sys – ; run-time – ) gforth-0.2 “DONE”
do-sys までのすべての LEAVE を解決します(訳注: loop, +loop , next 等の中で内部的に呼び出されます)。
標準では、 do-sys で CS-PICK
や CS-ROLL
を使用することは許可されていません。
MEM+DO
と MEM-DO
によって生成される do-sys を除いて、 Gforth では
CS-PICK
や CS-ROLL
の使用を許可しますが、 すべての ?DO
などに対して、
定義を介した任意の経路上に UNLOOP
が正確に 1 つだけ存在すること(LOOP
などの失敗経路上で
UNLOOP
コンパイルするなど)を確認するのはあなたの仕事です。 また、 すべての LEAVE
が(ループ終了ワードの 1
つまたは DONE
を使用して)解決されていることを確認する必要があります。