【问题标题】:Handle lambda-lists in method defining macros在定义宏的方法中处理 lambda 列表
【发布时间】:2015-04-02 11:26:04
【问题描述】:

例如,我想从 lambda 列表中获取所有变量来定义宏:

(defmacro my-defun (name lambda-list &body body &aux (fname (gensym)))
  `(progn
     (defun ,fname ,(all-vars lambda-list)
       ,@body)
     (defun ,name ,lambda-list
       (,fname ,@(all-vars lambda-list)))))

是否有一些函数可以使用 lambda 列表更轻松地完成此操作或其他事情,或者没有比编写这样的函数更简单的方法:

(defun all-vars (lambda-list)
  (mapcar (lambda (cons) (if (consp cons) (car cons) cons))
          (remove-if (lambda (symbol) (and (symbolp symbol)
                                           (char= (char (symbol-name symbol) 0) #\&)))
                     lambda-list)))

【问题讨论】:

  • 不是检查变量的第一个字符是否为#\&,而是检查符号是否在lambda-list-keywords中。
  • 即便如此,您也必须注意正确的语法和其他细微之处:lambda-list-keywords 的符号比函数的 lambda 列表中允许的符号多,顺序很重要,&optional 和 @987654326 @ 参数可能是一个缺点,它们在语法上有所不同,&aux 不是真正的参数,等等。
  • @PauloMadeira 绝对正确,尤其是关于 &aux 变量。但是,由于宏扩展包含(defun ,name ,lambda-list ...),因此实现必须检查 lambda 列表的正确性,因此在这种特殊情况中检查并不那么重要。

标签: lambda macros common-lisp


【解决方案1】:

这种方法你必须做的,或者需要找到一个已经实现它的库。有一些库可以处理 lambda-list 解析(并且每个实现都必须这样做,至少在内部)。这通常不会太难,只是乏味。

也就是说,您对 all-vars 的实现并不完全正确。 ordinary lambda lists 中关键字参数的完整规范是:

[&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]] 

这意味着你可以,例如,

(defun (&key ((:long-keyword-name x) "EX" xp))
  ...)

当您执行 (if (consp cons) (car cons) cons) 时,您会得到 (:long-keyword-name x),而不是 x。

这是我认为可行但尚未广泛测试的版本:

(defun lambda-list-variables (lambda-list)
  "Returns, in order, the variables declared in an ordinary lambda list,
but doesn't check that the lambda-list is actually legal."
  (labels ((ll-keyword-p (x)
             (member x lambda-list-keywords))
           (to-var (x)
             (if (symbolp x)
                 x             ; required, optional, rest, key, and aux 
                 (destructuring-bind (var &rest init-and-supplied) x
                   (declare (ignore init-and-supplied))
                   (if (listp var)
                       (second var)     ; key vars
                       var)))))         ; optional, key, and aux vars
    (remove-if #'ll-keyword-p
               (mapcar #'to-var lambda-list))))

CL-USER> (lambda-list-variables
          '(a b 
            &optional c (d 'dee) (e 'ee e-p)
            &rest f
            &key
            g
            (h 'aitch)
            (i 'eye i-p)
            ((:jay j))
            ((:kay k) 'kay)            
            ((:ell l) 'ell l-p)
            &aux 
            m
            (n 'en)))
;=> (A B C D E F G H I J K L M N)

【讨论】:

    【解决方案2】:

    我写了很多次这样的函数才发现它已经在 alexandria 库中了。

    (ql:quickload :alexandria)
    

    然后

    CL-USER> (alexandria:parse-ordinary-lambda-list '(a b c &optional x &key (ham 1 used-ham)))
    

    返回

    (A B C)
    ((X NIL NIL))
    NIL
    (((:HAM HAM) 1 USED-HAM))
    NIL
    NIL 
    T
    

    【讨论】:

    • 不适用于方法 lambda 列表 :(。但也许我应该更频繁地使用 alexandria。
    • @cl-porky11:真糟糕。很抱歉我没有用方法测试它
    猜你喜欢
    • 1970-01-01
    • 2011-01-26
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 2012-06-09
    • 1970-01-01
    • 2012-12-29
    相关资源
    最近更新 更多