このために言語に組み込まれているものは何もありません。 Rainer Joswig's answerは、ループがある程度の破壊を行うことができることを指摘していますが、それほど多くのことを行うわけではありません。この回答の以前のバージョンでは、破壊ラムダ リストをトラバースし、_ で始まるすべてのシンボルのリストを収集し、それらの変数を無視する宣言をフォームに追加することを提案しました。より安全なバージョンは、それぞれを新しい変数に置き換え (変数が繰り返されないようにするため)、それらをすべて無視します。したがって、次のようなもの
(destructuring-bind (_a (_b c)) object
c)
に展開します
(destructuring-bind (#:g1 (#:g2 c)) object
(declare (ignore #:g1 #:g2))
c)
このアプローチは、「 3.4.4.1.1 ラムダ リストによるデータ指向の破壊」で説明されている「データ指向」のみを使用している場合に問題なく機能します。ただし、「 3.4.4.1.2 ラムダ リストによるラムダ リスト指向の破壊」で説明されている「ラムダ リスト指向」アプローチを使用している場合は、&optional、&key などのラムダ リスト キーワードを使用できます。それらの一部の変数を置き換えてはならないため、はるかに複雑です。たとえば、
&optional (_x '_default-x)
後者はパターンではないため、_x何かに置き換えても問題ないかもしれませんが、そうではありません。_default-xしかし、Lisp ではコードはデータなので、destructuring-lambda-list をマップし、パターンである場所だけを置き換えるマクロを書くことができます。これは、まさにそれを行うやや毛むくじゃらのコードです。これは、関数と分解ラムダ リストを取り、引数の型 (全体、必須、オプションなど) と共に、ラムダ リスト内の各パターン変数に対して関数を呼び出します。
(defun map-dll (fn list)
(let ((result '())
(orig list)
(keywords '(&allow-other-keys &aux &body
&key &optional &rest &whole)))
(labels ((save (x)
(push x result))
(handle (type parameter)
(etypecase parameter
(list (map-dll fn parameter))
(symbol (funcall fn type parameter)))))
(macrolet ((parse-keyword ((&rest symbols) &body body)
`(progn
(when (and (not (atom list))
(member (first list) ',symbols))
(save (pop list))
,@body)))
(doparameters ((var) &body body)
`(do () ((or (atom list) (member (first list) keywords)))
(save (let ((,var (pop list)))
,@body)))))
(parse-keyword (&whole)
(save (handle :whole (pop list))))
(doparameters (required)
(handle :required required))
(parse-keyword (&optional)
(doparameters (opt)
(if (symbolp opt)
(handle :optional opt)
(list* (handle :optional (first opt)) (rest opt)))))
(when (and (atom list) (not (null list))) ; turn (... . REST)
(setq list (list '&rest list))) ; into (... &rest REST)
(parse-keyword (&rest &body)
(save (handle :rest (pop list))))
(parse-keyword (&key)
(doparameters (key)
(if (symbolp key)
(handle :key key)
(destructuring-bind (keyspec . more) key
(if (symbolp keyspec)
(list* (handle :key keyspec) more)
(destructuring-bind (keyword var) keyspec
(list* (list keyword (handle :key var)) more)))))))
(parse-keyword (&allow-other-keys))
(parse-keyword (&aux)
(doparameters (aux) aux))
(unless (null list)
(error "Bad destructuring lambda list: ~A." orig))
(nreverse result)))))
これを使用すると、_ で始まる各パターン変数を本体で無視される新しい変数に置き換える destructuring-bind* を簡単に作成できます。
(defmacro destructuring-bind* (lambda-list object &body body)
(let* ((ignores '())
(lambda-list (map-dll (lambda (type var)
(declare (ignore type))
(if (and (> (length (symbol-name var)) 0)
(char= #\_ (char (symbol-name var) 0)))
(let ((var (gensym)))
(push var ignores)
var)
var))
lambda-list)))
`(destructuring-bind ,lambda-list ,object
(declare (ignore ,@(nreverse ignores)))
,@body)))
次に、それが生成する展開を見てみましょう。
(macroexpand-1
'(destructuring-bind* (&whole (a _ . b)
c _ d
&optional e (f '_f)
&key g _h
&aux (_i '_j))
object
(list a b c d e f g)))
;=>
(DESTRUCTURING-BIND
(&WHOLE (A #:G1041 &REST B) C #:G1042 D
&OPTIONAL E (F '_F)
&KEY G #:G1043
&AUX (_I '_J))
OBJECT
(DECLARE (IGNORE #:G1041 #:G1042 #:G1043))
(LIST A B C D E F G))
置き換えてはいけない場所 (init フォーム、aux 変数など) は置き換えていませんが、必要な場所は処理しています。あなたの例でもこの作品を見ることができます:
(macroexpand-1
'(destructuring-bind* ((_ (_ . title)
(_ . description)
_
(_ . video-id)))
entry
(list video-id title description)))
;=>
(DESTRUCTURING-BIND ((#:G1044 (#:G1045 &REST TITLE)
(#:G1046 &REST DESCRIPTION)
#:G1047
(#:G1048 &REST VIDEO-ID)))
ENTRY
(DECLARE (IGNORE #:G1044 #:G1045 #:G1046 #:G1047 #:G1048))
(LIST VIDEO-ID TITLE DESCRIPTION))