为什么我的变量"未定义"?

时间:2015-02-12 01:30:35

标签: lisp

为什么我的变量nodes在vector-push-extend行中未定义?

(defun make_graph (strings)
  (defparameter nodes (make-array 0))
  (loop for x in strings do
       (vector-push-extend (make-instance 'node :data x) nodes))
n)

2 个答案:

答案 0 :(得分:4)

简短的回答是,您应该使用let代替defparameter来介绍您的变量。例如:

(defun make_graph (strings)
  (let ((nodes (make-array 0)))
    (loop for x in strings do
          (vector-push-extend (make-instance 'node :data x) nodes))
    ;; your code says N here, but I assume that's a typo...
    nodes))  

defparameter表单对于创建"特殊"非常有用。变量,有点类似于其他编程语言中的全局变量。 (存在一些差异,例如,defparameter引入的特殊变量并非完全全局 - 相反,它们是动态范围的,可以let绑定等...)

无论如何,let表单将改为创建一个局部变量。

答案 1 :(得分:4)

DEFPARAMETER用于顶层来定义全局特殊变量。

Toplevel为:

(defparameter *foo* 42)

仍处于顶层,因为PROGN内的形式仍处于顶层(根据定义):

(progn
  (defparameter *foo* 42)
  (defparameter *bar* 32))

不在顶级:

(defun baz ()
  (defparameter *foo* 42))

编译器不会将上一个表单识别为变量声明。但是当一个人调用(baz)并且函数正在运行时,就会定义并初始化变量。

编译器无法识别DEFPARAMETER的非顶层使用,但在运行时它将创建一个特殊的全局变量。

(defun make_graph (strings)
  (defparameter nodes (make-array 0))
  (loop for x in strings do
        (vector-push-extend (make-instance 'node :data x) nodes))
  n)

编译器发出警告:

;;;*** Warning in MAKE_GRAPH: NODES assumed special
;;;*** Warning in MAKE_GRAPH: N assumed special

因此,在上面的代码中,编译器不会将nodes识别为已定义的变量,如果它尚未在其他地方定义的话。函数中{em> use 的nodes会产生警告。

代码仍然可以工作,因为在运行时创建并初始化变量 - 但是对于每个函数调用。一遍又一遍。这个编译器还假设nodes就是这样:某种特殊变量。对于所有编译器,我仍然不会指望它。

n也未在任何地方定义。

备注

  • 引入本地词汇变量的正确方法是使用LETLET*(和其他绑定形式)

  • 使用DEFPARAMETER作为顶层形式。当它不是一种顶级形式时,这是不寻常的。通常作者会犯错误。