あなたのコードは、リストのすべての要素が数値であると想定しています。ただし、ネストされたリストがある場合、リストには数値のリストや数値のリストのリストなどを含めることができます。
リスト処理ループは、各要素のタイプを検査し、それに応じて処理する必要があります。
リストが表示された場合は、 を再帰的に呼び出して、get-smallest-large
そのリストから最小値と最大値を取得します。再帰呼び出しでは、これらの追加の 2 つのパラメーターを渡します。これにより、関数は、既に見たものよりも小さい最大値や大きい最小値を返さなくなります。
戻り値はコンスであるため、再帰呼び出しは次のようになります。
(destructuring-bind (sm . la) (get-smallest-large smallest largest)
(setf smallest sm largest la))
Common Lisp では、関数は複数の値を返すことができます。コンスとして数値のペアを返すコードは、複数の値をサポートしていない Lisp 方言からのコードの迅速かつ汚いポートのように見えます。
これは、返す(cons smallest largest)
(2 つの数値を保持するセルである単一の値を返す) 代わりに、返す(values smallest largest)
(実際には 2 つの値を返す) ことができることを意味します。値を使用する再帰呼び出しは、次のように要約されます。
(multiple-value-setq (smallest largest) (get-smallest-large smallest largest))
関数の先頭にある2 つのsetf
呼び出しは、再帰の正確性を損ないます。関数が指定され、入力時に値が指定されている場合、関数はsmallest
それらlargest
の値を尊重する必要があり、リスト構造から任意の値を取得して単純に上書きしてはなりません。そのコードには、それが数値であると仮定するという別の問題もあります(first lst)
。それはサブリストかもしれません!さらに別の問題: リストが空になる可能性があります!!!
次のようにすることをお勧めします。
(defun get-smallest-large (list &optional smallest largest)
;;
)
つまり、 defaultsmallest
およびlargest
tonil
でありnil
、コードの残りの部分では、「最小値または最大値がまだ不明」であることを示すものとして処理します。例えば:
;; the number we are looking at is smallest so far, if we have not
;; seen any number before, or if it is smaller than the smallest one we have
;; seen so far.
(if (or (null smallest) (< nxt smallest))
(setf smallest nxt))
これにより、別のバグも修正されます。空のリストで呼び出された場合、関数が何を返すべきかを考えてください。確かにそう(0 . 0)
ではありません。空のリストではゼロは発生しません。その場合に返す妥当な表現(nil . nil)
は、最小数と最大数が不明であることを示します。