LISP:SYSTEM::READ-EVAL-PRINT:变量I没有值

LISP:SYSTEM::READ-EVAL-PRINT:变量I没有值,lisp,common-lisp,Lisp,Common Lisp,我做了一个简单的测试函数,试图通过调用测试j10为j赋值。 我得到SYSTEM::READ-EVAL-PRINT:变量J没有值 (DEFUN test (j i) (LET ((j i)) (print j) ) ) 我想你误解了这些论点的作用。测试函数的当前形式有两个参数:j和i。你似乎只使用i,所以没有理由通过第二个。考虑 (defun test (i) (let ((j i)) (print j))) 然后简单地称之为test 10。我不确定

我做了一个简单的测试函数,试图通过调用测试j10为j赋值。 我得到SYSTEM::READ-EVAL-PRINT:变量J没有值

(DEFUN test (j i)
    (LET ((j i))
        (print j)
    )
)

我想你误解了这些论点的作用。测试函数的当前形式有两个参数:j和i。你似乎只使用i,所以没有理由通过第二个。考虑

(defun test (i)
  (let ((j i))
    (print j)))

然后简单地称之为test 10。

我不确定您是否知道let中的变量j和test参数中定义的变量j是两个完全不同的变量。真正的重构是重命名它们,这样就很清楚了,下面是消除歧义的代码:

(defun test (pj pi)
  (let ((lj pi))
    (print lj)))
这是完全相同的代码,因为let中的新绑定有效地使具有相同名称的旧变量在let期间不可访问。警告将是关于PJ的,因为它在函数中仍然未被使用,因此您应该考虑移除它:

(defun test (pi)
  (let ((lj pi))
    (print lj)))
通常有一个未使用的参数是一个bug,所以CL会唠叨这些事情。但是,有时您会生成使用传递值的函数的高阶函数。例如

(defun map-assoc (fn lst)
  (loop :for n :from 0
        :for (k . v) :in lst
        :collect (funcall fn k v n lst)))
现在想象一下,你只想列一张钥匙的清单

(defun assoq-keys (lst)
  (map-assoc (lambda (k v n lst)
               (declare (ignore v n lst))
               k)
             lst))

(assoq-keys '((I . 1) (II . 2) (III . 3) (IV . 4) (V . 5)))
; ==> (I II III IV V)

declare语句将消除传递的其他变量将不被使用的错误,但您必须包含它们,因为这是map assoc函数所做的约定。对于接受其值或交换其他值的函数,您将使用其他变量,因此传递值是一个很好的泛化。

当您执行测试j 10时,您希望该调用中的值j等于什么?我希望在计算/执行函数测试之前,在您的函数调用测试j 10中,它等于10,lisp首先必须计算参数。那么在调用函数测试之前,j的值是多少?你是用defparameter还是什么设置的?如果没有,为什么要使用它?这就像调用一个尚未编写的函数。若在函数调用中使用,符号必须求值为某个值。我想补充一点,“let”创建它自己的作用域。所以你有全局作用域,函数作用域和let作用域。在这种情况下,let作用域'j'和'i'隐藏着函数作用域'i'和'j'。