(rontolisp) docs
← マクロ

loop

(loop clause...) (拡張形式) または (loop form...) (単純形式)

ANSI loop マクロの限定的なサブセットです。既存の反復コア (内部のブロック境界で包まれた do* 相当のステップ処理) に展開されるため、インタプリタと両コンパイラで同一に動作します。

トップレベルの各サブ形式がすべて複合形式 (リスト) の場合、loop単純ループ になります。それらの形式を return で脱出するまで永遠に繰り返します。

それ以外の場合は、節 (clause) から構成される 拡張ループ です。サポートする節は次のとおりです。

  • 数値ステップ: for VAR from LO [to|upto|below|downto|above HI] [by STEP] (upfrom/downfrom も可。from のない上限キーワードは 0 から開始)。
  • リストステップ: for VAR in LIST [by FN]for VAR on LIST [by FN] (VAR には分配束縛パターンも指定可能)。
  • シーケンスステップ: for VAR across SEQVAR を文字列の各文字、またはベクタの各要素に順に束縛します。
  • 汎用ステップ: for VAR = INIT [then STEP] (VAR には分配束縛パターンも指定可能)。
  • ローカル変数: with VAR [= INIT] (VAR には分配束縛パターンも指定可能。and で連結した with の束縛は並行に行われます)。
  • 型宣言: for/as/withVAR の直後、残りの節より前に、ANSI のいずれかの綴りによる型宣言を省略可能な要素として受け付けます — of-type TYPE の明示形式、または fixnum/float/t/nil の単純形式です。rontolisp の loop 展開は型を扱わないため、この宣言は解析されるだけで意味を持ちません。
  • 集約: collectappendnconcsumcountmaximizeminimize。それぞれ省略可能な into VAR を取れます。
  • 終了判定: thereis EXPRalways EXPRnever EXPR
  • 制御: while/until (記述位置で判定)、repeat Ndo FORM...return EXPR、本体形式内の (loop-finish)initially FORM...finally FORM...、および条件節 when/if/unless (省略可能な elseend を伴い、選択された節では判定値を it で参照可能)。

複数の for 節は一緒にステップし、最も短いドライバが尽きた時点でループは終了します。これがインデックス付き map の定石です。逐次的な節は順にステップします (後の節の初期化式・ステップ式は直前の節が生成した値を参照でき、最初に尽きたドライバでステップは停止するため、for x in xs for a = (f x) then (g a x) は CL と同様に動作します)。

andfor 節をひとつのグループに連結し、初期化とステップを前回の反復の値に対して計算します (do* に対する do の並行ステップに相当)。

for 変数は反復ごとに作り直される束縛ではなく、その場でステップされるひとつの束縛です。したがってループ終了後も最後の反復の値を保持しており、それが finally の見る値であり、本体で生成したクロージャを後から呼び出したときに返る値でもあります (dolist は反復ごとに新しく束縛するため、そのクロージャはそれぞれの要素を保持します。両者は異なるのが正しい挙動です)。

変数への代入はその節自身の終了判定を通過した後にのみ行われます。そのため、ドライバが尽きた節は最後の値を保持したまま、要素が残っていた手前の節だけがステップします。

for VAR on は一見例外に見えますが例外ではありません。この場合、変数そのものがカーソルなので nil で終わるのが正しい挙動です。being the hash-key/hash-value の変数も同様です。数値変数は上限を 1 ステップ超えた値で終わります。

集約と数値範囲はよくあるケースを直接表現できます。

本体節の後 (または in/on/across のように本体の先頭で変数を代入する for の後) に置いた while/until は、その記述位置で判定するため、現在の要素を参照できます。

when/if/unless の内側では、アナフォリックな it で判定式の値を参照できます。

thereis は式が最初に非 nil になった値を返します。always/never は最初の失敗で nil に短絡し、正常終了時には t を返します。これらによる早期脱出は return と同様に finally をスキップします。

本体形式内の (loop-finish) は反復を正常終了させます。finally は実行され、ループの結果値も生成されます (両方をスキップする return とは異なります)。

for/with の変数には分配束縛パターン — 変数のリスト (ネスト可能。nil はその位置を無視) — を指定できます。

ドットパターンはリストの残りを束縛するため、連想リストをそのまま走査できます。

for ... across は文字列を 1 文字ずつ、またはベクタを 1 要素ずつ走査します。

for VAR being {the|each} {hash-keys|hash-key|hash-values|hash-value} {of|in} TABLE はハッシュテーブルを反復します。using (hash-value V) (または using (hash-key K)) でもう一方を束縛できます。

この節はテーブルのスナップショットを取ってそれを走査するため、反復順序はテーブルのものになり、本体でテーブルを変更しても進行中の走査には影響しません。

being のパッケージ形式 — for VAR being {the|each} {symbols|present-symbols|external-symbols} {of|in} PACKAGE — は受け付けますが 簡易版 です: rontolisp には実行時のインターンテーブルがないため、この節は解析され のシーケンスを反復します。本体は実行されず、集約結果は nil になります。ロード時にパッケージを走査するライブラリ (cl-who の hyperdoc テーブルなど) がエラーなくロードできるようにするためのものです:

変数の後の型宣言は、どちらの綴りでも受け付けられ、無視されます。

制限事項: named/return-from は未対応です。分配束縛パターンはラムダリストキーワードを認識しません (&optional などはエラーにはならず、通常の変数として束縛されます)。(loop-finish) は文の位置 (式の途中は不可) に置く必要があり、ネストした反復形式の内側では使えません。thereis/always/never はデフォルト結果への集約とは併用できません (into を使ってください)。into を伴わない集約節はすべて同種でなければならず、収集系の節は結果リストをソース順に構築します。