Lisp 有什么好方法可以在解构绑定中声明未使用的变量吗?

Lisp 有什么好方法可以在解构绑定中声明未使用的变量吗?,lisp,pattern-matching,common-lisp,destructuring,Lisp,Pattern Matching,Common Lisp,Destructuring,我不知道,有没有办法在erlang中添加类似u的内容,用于解构绑定中的“未使用的值” 例如,我们有这样的东西: (destructuring-bind ((_SNIPPET (_TITLE . title) (_DESCRIPTION . description) _RESOURCE-ID (_VIDEO-ID

我不知道,有没有办法在erlang中添加类似u的内容,用于解构绑定中的“未使用的值”

例如,我们有这样的东西:

(destructuring-bind ((_SNIPPET
                               (_TITLE . title)
                               (_DESCRIPTION . description)
                               _RESOURCE-ID (_VIDEO-ID . video-id)))) entry
                (declare (ignore 
                          _SNIPPET _TITLE _DESCRIPTION _RESOURCE-ID _VIDEO-ID))
                (list video-id title description)))
 (destructuring-bind ((_
                         (_ . title)
                         (_ . description)
                         (_ (_ . video-id)))) entry
                    (list video-id title description)))
最好不要为每个未使用的值都设置特定的变量,并编写如下内容:

(destructuring-bind ((_SNIPPET
                               (_TITLE . title)
                               (_DESCRIPTION . description)
                               _RESOURCE-ID (_VIDEO-ID . video-id)))) entry
                (declare (ignore 
                          _SNIPPET _TITLE _DESCRIPTION _RESOURCE-ID _VIDEO-ID))
                (list video-id title description)))
 (destructuring-bind ((_
                         (_ . title)
                         (_ . description)
                         (_ (_ . video-id)))) entry
                    (list video-id title description)))

有没有任何方法可以通过standart解构bind或任何其他standart宏获得这种行为?或者我必须使用一些类似ML的模式匹配库,如果是这样的话-哪一个?

使用
解构-BIND
是不可能的(一个变量不能多次使用,一些编译器会抱怨)。您可以枚举变量,
\u 1
\u 2
。。。但是你必须忽略它们中的每一个

循环
可以做到:

CL-USER 23 > (loop for ((a b nil c) nil d) in '(((1 2 3 4) 5 6)
                                                ((1 2 3 4) 5 6))
                   collect (list a b c d))
((1 2 4 6) (1 2 4 6))
NIL
用作通配符变量

您可以重用
循环
宏:

(defmacro match-bind (pattern object &body body)
  `(loop with ,pattern = ,object
         while nil
         finally (return (progn ,@body))))

CL-USER 37 > (match-bind ((a b nil c) nil d)
                 '((1 2 3 4) 5 6)
               (list a b c d))
(1 2 4 6)
您可以使用一些库中的
LET-MATCH
。例如:
可能还有更奇特的版本。

该语言中没有为此内置任何内容。指出它可以进行一些解构,但几乎没有这么多。在一个示例中,我建议遍历分解lambda列表,收集以u开头的所有符号的列表,并在表单中添加声明以忽略这些变量。一个更安全的版本用一个新的变量替换每个变量(这样就没有重复的变量),并忽略所有变量。因此

(destructuring-bind (_a (_b c)) object
  c)
将扩展到

(destructuring-bind (#:g1 (#:g2 c)) object
  (declare (ignore #:g1 #:g2))
  c)
如果您只使用中所述的“数据导向”,则此方法可以正常工作。但是,如果您使用中描述的“lambda list directed”方法,您可以使用lambda list关键字,如&optional、&key等,那么事情就复杂得多,因为您不应该在其中的某些部分替换变量。例如,如果你有

&optional (_x '_default-x)
然后可以用某物替换
\ux
,但不能替换
\udefault-x
,因为后者不是一种模式。但是,在Lisp中,代码就是数据,因此我们仍然可以编写一个宏,该宏映射到分解lambda列表,并仅替换为模式的位置。这里有一些毛茸茸的代码就是这样做的。这将获取一个函数和一个分解lambda列表,并为lambda列表中的每个模式变量以及参数类型(整型、必需型、可选型等)调用该函数

使用它,编写一个解构绑定*非常容易,它用一个新的变量替换以u开头的每个模式变量,该变量将在主体中被忽略

(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))

循环的解构不允许destructuring-BIND所做的一切。例如,循环不支持关键字、可选参数、rest参数和辅助参数。虽然这对OP的情况有效,但这不会“替代”使用解构绑定的代码。@JoshuaTaylor:
循环
模式与
解构绑定
模式不同。在我看来,他并不是专门寻找一个
解构绑定
替代品,因为他还要求其他“标准宏”或模式匹配库(在这些库中,提供类似
解构绑定
的模式接口是不寻常的)。既然@coredump接受了答案,我不得不假设它适用于手头的案件。然而,问题的标题是“在解构绑定中声明未使用变量的好方法吗?”如果有人编写了
(解构绑定(((ux&rest-xs)((uy&可选z))
,并且想忽略
(ux
)和
(uy
),那么执行
(匹配绑定((nil&rest-xs)(nil&可选z))…)
。它将适用于很多情况,但不是所有解构绑定都适用。我只是想弄清楚区别。@JoshuaTaylor:没错,但你的例子也不起作用。它无法理解解构bind的语法。是的,Vassil Nikolov提出了3.1.1.,但之后也有更多的讨论,在处理参数时,后面的initforms可以引用前面的变量,因此也有一些中间环境在起作用。无论如何,我链接到的第二个线程足以说明“这可能是允许的,也可能是不允许的。”在我写答案的过程中,我开始编写一些lambda列表解析代码,其中的一些细节很难确定感谢(一如既往,有用的)反馈!无法映射到分解绑定lambda列表并替换符号。lambda列表有一个语法。我们需要考虑到这一点。示例:
(解构绑定**(a&aux(b')))(1)(列表a-b))
->
(1:G1133)
第一个可以声明忽略带有下划线的变量,可以使用下划线:
(解构绑定*(a&aux(#a')))(1)(列表a-a))
。它还可以声明忽略在解构绑定lambda列表中不存在的变量。首先:考虑嵌套调用,其中嵌套调用在键、可选或辅助绑定中完成。外部宏将从嵌套调用中提取嵌入的模式变量。。。第二:如果将
*在警告时中断*
(如在LispWorks中)设置为true,则额外忽略的变量可能会在编译期间触发中断-当编译器发出警告时。第三:如果变量不在模式中,可以使用下划线…@RainerJoswig您完全正确。我已经完全修改了这个答案来解析分解lambda列表,并且只重命名发生在模式位置的变量。不必再担心在init表单或aux变量等中进行替换。尽管实现中存在任何错误,我认为在thi中的方法