首页 > 解决方案 > 无法更改列表

问题描述

例如,我有stack-push将元素推入堆栈的函数。

(defun stack-push (stack element)
    (if (not (listp element))
        (setf stack (cons element stack))
        (dolist (current-el (reverse element))
            (setf stack (cons current-el stack)))))

但是当我像 一样调用它时(stack-push *some-stack* '(a b c d e)),它不会影响 on *some-stack*。你能解释一下为什么吗?

标签: lispcommon-lispclisp

解决方案


setf用一个符号作为地方 like(setf stack (cons element stack))扩展为(setq stack (cons element stack)). 这通常在您创建函数时发生。你的功能变成了这样:

(defun stack-push (stack element)
  (if (not (listp element)
      (setq stack (cons element stack))
      (dolist (current-el (reverse element))
        (setq stack (cons current-el stack)))))

请注意,我仅setf在此处进行了扩展。两者都defun变得dolist非常可怕的扩展,使代码更难读。系统完全扩展了表单,因此运行的代码没有宏。

setq更新绑定,因此当它更新时,它更新stack的是它,而不是*some-stack*. 如果你这样做,这正是发生的事情(stack-push *some-stack* "a")

  1. 你传递函数 *some-stack*"a". 它评估它的论点。
  2. *some-stack*评估为具有cons单元格的地址 A,例如。("b").
  3. "a"是位于地址 B 的文字
  4. 参数被绑定到新的绑定。stack指向A,元素指向B。
  5. 由于element指向 B(not (listp element)) ; ==> t并且代码遵循结果。
  6. (setf stack (cons element stack))运行时之前更改为 (setq stack (cons element stack)).
  7. (cons element stack)cons以 B 和 A 作为参数调用。返回地址为 C 的新单元格("a" "b")
  8. setq更新以便绑定stack指向 C。
  9. stack-push返回最后一个评估值,它是setq第二个参数 C的结果。

在函数中没有提到*some-stack*. 绑定永远不会被引用或更新。只有stack更新指向的新值。

由于这是一个函数,因此您可以使用文字调用您的函数,如果参数是要更新的变量,这通常不起作用。

(stack-push '("b") "a") ; ==> ("a" "b")

有一种形式叫push. 它不是一个函数。你可以看到它做了什么:

(macroexpand '(push "a" *some-stack*)) 
; ==> (setq *some-stack* (cons "a" *some-stack*))

因此,push要工作,您需要第二个参数是setf可以的。尝试使用文字会扩展为无法运行的代码。对于类型,您可以制作自己的setf扩展器,这样就setf可以了push


推荐阅读