在Common Lisp中将变量列表评估为值列表

时间:2019-05-31 20:24:37

标签: common-lisp

我想知道如何实现以下目标。假设我有一个受上述let约束的变量列表。我想将此列表变成这些变量绑定到的值的列表。

也就是说,假设我们有

(define make-plist-from-variables (variables)
  (let ((keys variables)
        (values (mapcar #'identity variables)))
    (if (eq (length keys) (length values))
     (make-plist keys values)
     nil))))

我可以用什么代替#'identity来正确解压这些值?

此刻,以下调用将产生以下输出。

CL-USER> (let ((a 2) (b 3)) (make-plist-from-variables '(a b)))
(A A B B)

我希望它是(A 2 B 3)

2 个答案:

答案 0 :(得分:4)

它必须是一个宏,因为无法根据其符号获取变量的词法值。

(defmacro make-plist-from-variables (&rest variables)
  (loop :for binding :in variables
        :collect `',binding :into result
        :collect binding :into result
        :finally (return `(list ,@result))))

(macroexpand-1 '(make-plist-from-variables a b))
; ==> (list 'a a 'b b) 

(let ((a 2) (b 3)) 
  (make-plist-from-variables a b))
; ==> (a 2 b 3)

编辑

在没有loop的情况下使用mapcan的实现:

(defmacro make-plist-from-variables (&rest variables)
  `(list ,@(mapcan (lambda (v) `(',v ,v)) variables))

答案 1 :(得分:2)

函数无法访问其调用者的词法环境。 更准确地说,在求值过程中,您无法仅知道其符号就访问词法变量的值。只有宏有权访问environment objects

特殊变量

您可以使用动态绑定:

(defun foo ()
  (declare (special a))
  (symbol-value 'a))

(let ((a 3))
  (declare (special a))
  (foo))

=> 3

对于您而言,您可以通过在所有符号上使用SYMBOL-vaLUE来收集符号及其值。

与您的问题有关的是如何动态地将变量绑定到在评估时已知变量名称和/或值的值;参见特殊运算符PROGV

您可以获得例如通过编写以下代码创建关联列表:

(acons 'a a (acons 'b b nil))

根据问题背后的用例,您可能希望有一个宏扩展为此类代码,该宏引用要评估的变量。