関数定義用のパーツを作成しました。あとはこれらを組み合わせるだけです。
まず、while 式の内容:
(while (<= row-number number-of-rows) ; true-or-false-test (setq total (+ total row-number)) (setq row-number (1+ row-number))) ; incrementer
let 式 varlist
と合わせて、これで関数定義の本体はほぼ完成です。ただし、最後にもう一つ要素が必要ですが、その必要性はやや分かりにくいものです。
仕上げとして、while 式の後に変数 total を単独の行に配置します。そうしないと、関数全体で返される値は、let の本体内で評価される最後の式の値となり、これが while によって返される値、すなわち常に nil
となります。
一見しただけでは分かりにくいかもしれません。増分式が関数全体の最後の式であるかのようにさえ見えます。しかし、その式はwhileの本体の一部であり、記号whileから始まるリストの最後の要素なのです。さらに、whileのループ全体は、letの本体内にあるリストとなっています。
概略としては、関数は次のようになります。
(defun name-of-function (argument-list)
"documentation..."
(let (varlist)
(while (true-or-false-test)
body-of-while... )
... )) ; Need final expression here.
let を評価した結果が defun
によって返されることになります。というのも、let は defun 全体を除いて、いかなる包含リストの中にも埋め込まれていないからです。しかし、もし while が let 式の最後の要素である場合、関数は常に
nil を返します。これは意図した動作ではありません!代わりに欲しいのは、変数 total の値です。これは、let
から始まるリストの最後の要素として単にこのシンボルを配置することで返されます。これはリストの直前の要素が評価された後に評価されるため、合計値として正しい値が代入された後に評価されることになります。
letから始まるリストをすべて1行に出力すると、より分かりやすくなるでしょう。この形式であれば、varlistとwhileの式がletから始まるリストの2番目と3番目の要素であり、totalが最後の要素であることが一目瞭然です:
(let (varlist) (while (true-or-false-test) body-of-while... ) total)
すべてをまとめると、triangle の関数定義は次のようになります。
(defun triangle (number-of-rows) ; Version with ; incrementing counter. "Add up the number of pebbles in a triangle. The first row has one pebble, the second row two pebbles, the third row three pebbles, and so on. The argument is NUMBER-OF-ROWS."
(let ((total 0)
(row-number 1))
(while (<= row-number number-of-rows)
(setq total (+ total row-number))
(setq row-number (1+ row-number)))
total))
関数を評価して triangle
をインストールしたら、実際に試してみることができます。以下に2つの例を挙げます。
(triangle 4) (triangle 7)
最初の4つの数の和は10で、最初の7つの数の和は28です。