数値リストの要素を出力する while ループの例は、再帰的に記述できます。以下に、変数 animals の値をリストに設定する式を含めたコードを示します。
EmacsのInfoでこれを読んでいる場合は、この式をInfo内で直接評価できます。そうでない場合は、例を*scratch*バッファにコピーし、そこで各式を評価する必要があります。C-u C-x C-eを使用して(print-elements-recursively
animals)式を評価し、結果がバッファに出力されるようにしてください。そうしないと、Lispインタプリタが結果をエコーエリアの1行に無理やり詰め込もうとします。
また、print-elements-recursively
関数の最後の閉じ括弧の直後、コメントの前にカーソルを置いてください。そうしないと、Lisp インタプリタがコメントを評価しようとしてしまいます。
(setq animals '(gazelle giraffe lion tiger)) (defun print-elements-recursively (list) "Print each element of LIST on a line of its own. Uses recursion." (when list ; do-again-test (print (car list)) ; body (print-elements-recursively ; recursive call (cdr list)))) ; next-step-expression (print-elements-recursively animals)
print-elements-recursively
関数はまず、リストに内容があるかどうかを確認します。内容がある場合、関数はリストの最初の要素である CAR
を出力します。次に、関数は自身を呼び出しますが、引数として渡すのはリスト全体ではなく、リストの2番目以降の要素である CDR です。
言い換えれば、リストが空でない場合、関数は初期コードと類似しているが、実行スレッドが異なり、最初のインスタンスとは異なる引数を持つ別のコードインスタンスを呼び出します。
別の言い方をすれば、リストが空でない場合、1台目のロボットが2台目のロボットを組み立てて指示を出します。2台目のロボットは1台目とは別の個体ですが、モデルは同じです。
2回目の評価が行われる際、when
式が評価され、真であれば引数として受け取ったリストの最初の要素(元のリストの2番目の要素)を出力します。その後、関数は自身が呼び出された際のリストの
CDR を引数として自身を再呼び出しします。これは(2回目は)元のリストの CDR の CDR となります。
なお、関数が「自身を呼び出す」と述べていますが、ここで意味しているのは、Lispインタプリタがプログラムの新しいインスタンスを組み立てて指示を出すということです。この新しいインスタンスは最初のもののクローンですが、独立した個体です。
関数が自身を呼び出すたびに、元のリストの短縮版に対して呼び出しが行われます。これにより、短縮されたリストを処理する新しいインスタンスが生成されます。
最終的に、この関数は空のリストに対して自身を呼び出します。引数に nil
を持つ新しいインスタンスを生成します。条件式は list の値を判定します。list の値は nil であるため、when の式は false と判定され、then 節は評価されません。その後、関数全体として nil を返します。
*scratch* バッファ内で式 (print-elements-recursively animals)
を評価すると、次のような結果になります。
gazelle giraffe lion tiger nil