Lisp 中的赋值

Assignment in Lisp

我在 Common Lisp 中有以下设置。 my-object 是 5 个二叉树的列表。

(defun make-my-object ()
    (loop for i from 0 to 5
          for nde = (init-tree)
          collect nde))

每个二叉树都是一个大小为 3 的列表,其中有一个 node、一个左 child 和一个右 child

(defstruct node
    (min 0)
    (max 0)
    (ctr 0))

(defun vals (tree)
    (car tree))

(defun left-branch (tree)
    (cadr tree))

(defun right-branch (tree)
    (caddr tree))

(defun make-tree (vals left right)
    (list vals left right))

(defun init-tree (&key (min 0) (max 1))
    (let ((n (make-node :min min :max max)))
        (make-tree n '() '())))

现在,我试图手动将一个元素添加到其中一个二叉树中,如下所示:

(defparameter my-object (make-my-object))
(print (left-branch (car my-object))) ;; returns NIL
(let ((x (left-branch (car my-object))))
    (setf x (cons (init-tree) x)))
(print (left-branch (car my-object))) ;; still returns NIL

第二次调用print还是returnsNIL。为什么是这样?如何向二叉树添加元素?

第一个函数就是:

(defun make-my-object ()
  (loop repeat 5 collect (init-tree)))

现在您为 node 定义了一个结构,但是您为 treemy-object 使用了一个列表?为什么它们不是结构体?

而不是 carcadrcaddr 人们会使用 firstsecondthird.

(let ((x (left-branch (car my-object))))
   (setf x (cons (init-tree) x)))

您将局部变量 x 设置为新值。为什么?在 let 之后,局部变量也消失了。你为什么不设置左分支呢?您需要定义一种方法来这样做。记住:Lisp 函数是 return 值,而不是你以后可以设置的内存位置。如何更改列表中的内容?更好的是:使用结构并更改插槽值。该结构(甚至 CLOS 类)与普通列表相比具有以下优点:对象带有类型,槽被命名,访问器被创建,make 函数被创建,类型谓词被创建,...

无论如何,我会为节点、树和对象...

定义结构或CLOS 类

这个问题中的大部分代码对于这里的实际问题来说并不是必不可少的。真正的问题在于对这段代码的误解:

(let ((x (left-branch (car my-object))))
  (setf x (cons (init-tree) x)))

我们可以在没有任何类型的用户定义结构的情况下看到相同类型的行为:

(let ((cell (cons 1 2)))
  (print cell)            ; prints (1 . 2)
  (let ((x (car cell)))
    (setf x 3)
    (print cell)))        ; prints (1 . 2)

如果您理解为什么两个 print 语句都产生 (1 . 2),那么您就足以理解为什么您自己的代码没有执行您之前的操作希望它能做到。

这里有两个变量:cellx。我们关注三个值 12,以及调用 (cons 1 2)。 Lisp 中的变量通常称为 bindings;变量或名称绑定到一个值。变量 cell 绑定到 cons 单元格 (1 . 2)。当我们进入内部 let 时,我们评估 (car cell) 以产生值 1,然后是 bound 到变量 x。然后,我们将一个新值 3 赋给变量 x。这不会修改包含 x 最初绑定到的值的 cons 单元格。事实上,最初绑定到 x 的值是由 (car cell) 产生的,一旦调用 (car cell) 返回,唯一重要的值是 1

如果你有一些其他编程语言的经验,这直接类似于

int[] array = ...;
int x = array[2];    // read from the array; assign result to x
x = 42;              // doesn't modify the array

如果要修改结构,需要setf结构的相应部分。例如:

(let ((cell (cons 1 2)))
  (print cell)            ; prints (1 . 2)
  (setf (car cell) 3)
  (print cell))           ; prints (3 . 2)