それらが何をするかという点では、carとcdrはfirstとrestと同等です。これは、ドキュメントで非常に明確です。HyperSpec は、first、second、 &cのエントリについて次のように述べています。
最初、2 番目、3 番目、4 番目、5 番目、6 番目、7 番目、8 番目、9 番目、10 番目の関数は、それぞれ list の 1 番目、2 番目、3 番目、4 番目、5 番目、6 番目、7 番目、8 番目、9 番目、10 番目の要素にアクセスします。具体的には、
(first list) == (car list)
(second list) == (car (cdr list))
(third list) == (car (cddr list))
…
ノート:
1 番目は機能的に car と同等、2 番目は機能的に cadr と同等、3 番目は機能的に caddr と同等、4 番目は機能的に cadddr と同等です。
これらの関数を使用する場合、機能ではなくスタイルに違いがあります。これは実際にはHyperSpecでも呼び出されます。たとえば、restのエントリで:
ノート:
議論がコンスとしてではなくリストとして主観的に見られることである場合、restはしばしばcdrよりもスタイル的に好まれます。
たとえば、コンス セルから構築された構造体をマッピングする 2 つの方法を考えてみましょう。最初に、コンス セルのツリーにマッピングし、ツリーの各リーフ (つまり、非コンス) で何らかの関数を呼び出します。conspで何かが cons であるかどうかをチェックし、cons である場合はそのcarとcdrに再帰します。consを呼び出して、結果を新しいコンス セルに結合します。
(defun map-over-cons (function tree)
(if (not (consp tree))
(funcall function tree)
(cons (map-over-cons function (car tree))
(map-over-cons function (cdr tree)))))
別の方法として、リストをマッピングする場合、通常はendp (またはnullですが、endpは単にnilを探すのではなく、リストの終わりを探していることを強調しています) を使用して終了条件をチェックし、関数を呼び出します。リストの最初に戻り、リストの残りの部分に再帰します。consを使用して構築された結果を見るのはかなり一般的ですが、実際にはlist*があり、リストが構築されていることを強調する 2 つの引数で呼び出されたときに同じタスクを実行します (一般に、もう少し多くのことができます) 。
(defun map-over-list (function list)
(if (endp list)
'()
(list* (funcall function (first list))
(map-over-list function (rest list)))))
これらの関数のいずれも、 car 、 cdr 、および cons を使用するか、 first 、 rest 、および list* 、またはそれらの任意の組み合わせを使用して記述できますが、いずれか一方に固執すると、後でコードを読む可能性のある人々に役立ちます(オリジナルを含む)著者)、および著者の意図を示します。
また、最終的に CDR、CAR を使用して FIRST/REST を実装するにはどうすればよいですか?
どうですか:
(defun first (x) (car x))
(defun rest (x) (cdr x))
または、 symbol-functionがある場合はさらに良いでしょう:
(setf (symbol-function 'first) (symbol-function 'car))
(setf (symbol-function 'rest) (symbol-function 'cdr))