Lisp Can';更改列表

Lisp Can';更改列表,lisp,common-lisp,clisp,Lisp,Common Lisp,Clisp,例如,我有一个函数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 p

例如,我有一个函数
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*”(abcde))那样调用它时,它不会影响
*some stack*
。你能解释一下原因吗?

setf
带有一个类似于
(setf堆栈(cons元素堆栈))
的符号扩展为
(setq堆栈(cons元素堆栈))
。这通常在创建函数时发生。您的功能如下:

(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*“a”)
,则会发生以下情况:

  • 您传递函数
    *一些堆栈*
    “a”
    。它评估其论点
  • *某些堆栈*
    的计算结果是地址A有一个
    cons
    单元格,例如
    (“b”)
  • “a”
    是驻留在地址B的文本
  • 参数被绑定到新绑定<代码>堆栈
  • 指向A,元素指向B
  • 因为
    元素
    指向B
    (不是(listp元素));=>t
    ,代码跟随其后的结果
  • (setf堆栈(cons元素堆栈))
    运行时之前更改为
    (setq堆栈(cons元素堆栈))
  • (cons元素堆栈)
    使用B和A作为参数调用
    cons
    。返回地址为C的新单元格
    (“a”“b”)
  • setq
    更新,以便绑定
    stack
    指向C
  • stack push
    返回最后一个计算值,该值是第二个参数C
    setq的结果
  • 函数中没有提到
    *某些堆栈*
    。绑定从未被引用或更新。只有
    stack
    更新为指向的新值

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

    (stack-push '("b") "a") ; ==> ("a" "b")
    
    有一个叫做
    push
    的表单。它不是一个函数。您可以看到它的功能:

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

    因此,要使
    push
    工作,您需要第二个参数是
    setf
    -able。使用文本进行尝试会扩展为无法命名的代码。对于类型,您可以制作自己的
    setf
    扩展器,以便
    setf
    push
    工作

    setf
    带有符号,如
    (setf堆栈(cons元素堆栈))
    扩展为
    (setq堆栈(cons元素堆栈))
    。这通常在创建函数时发生。您的功能如下:

    (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*“a”)
    ,则会发生以下情况:

  • 您传递函数
    *一些堆栈*
    “a”
    。它评估其论点
  • *某些堆栈*
    的计算结果是地址A有一个
    cons
    单元格,例如
    (“b”)
  • “a”
    是驻留在地址B的文本
  • 参数被绑定到新绑定<代码>堆栈指向A,元素指向B
  • 因为
    元素
    指向B
    (不是(listp元素));=>t
    ,代码跟随其后的结果
  • (setf堆栈(cons元素堆栈))
    运行时之前更改为
    (setq堆栈(cons元素堆栈))
  • (cons元素堆栈)
    使用B和A作为参数调用
    cons
    。返回地址为C的新单元格
    (“a”“b”)
  • setq
    更新,以便绑定
    stack
    指向C
  • stack push
    返回最后一个计算值,该值是第二个参数C
    setq的结果
  • 函数中没有提到
    *某些堆栈*
    。绑定从未被引用或更新。只有
    stack
    更新为指向的新值

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

    (stack-push '("b") "a") ; ==> ("a" "b")
    
    有一个叫做
    push
    的表单。它不是一个函数。您可以看到它的功能:

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

    因此,要使
    push
    工作,您需要第二个参数是
    setf
    -able。使用文本进行尝试会扩展为无法命名的代码。对于类型,您可以制作自己的
    setf
    扩展器,以便
    setf
    push
    工作

    请注意Sylvester的回答,这里有一个版本的
    堆栈推送
    ,乍一看是正确的,但实际上有一个丑陋的问题(感谢jkiiski指出了这一点!),然后是一个仍然存在问题的简单版本,最后是一个不存在问题的简单版本的变体

    这是最初的版本。这与您的签名一致(它要么接受一个不能成为列表的参数,要么接受一个参数列表,并根据它看到的内容决定要做什么)

    然而,我更倾向于使用
    &rest
    参数编写它,如下所示。这个版本比较简单,因为它
    ? (macropp '(stack-push* (foo (a)) 1) 2)
    -- (stack-push* (foo (a)) 1)
    -> (setf (foo (a)) (append (reverse (list 1)) (foo (a))))
    -> (let ((#:g86139 (a)))
         (funcall #'(setf foo) (append (reverse (list 1)) (foo (a))) #:g86139))
    
    (defun stackify (s &rest elements)
      (append (reverse elements) s))
    
    (define-modify-macro stack-push* (s &rest elements)
      stackify)
    
    ? (macropp '(stack-push* (foo (a)) 1) 2)
    -- (stack-push* (foo (a)) 1)
    -> (let* ((#:g86170 (a)) (#:g86169 (stackify (foo #:g86170) 1)))
         (funcall #'(setf foo) #:g86169 #:g86170))
    -> (let* ((#:g86170 (a)) (#:g86169 (stackify (foo #:g86170) 1)))
         (funcall #'(setf foo) #:g86169 #:g86170))
    
    (defun macropp (form &optional (n 1))
      (let ((*print-pretty* t))
        (loop repeat n
              for first = t then nil
              for current = (macroexpand-1 form) then (macroexpand-1 current)
              when first do (format t "~&-- ~S~%" form)
              do (format t "~&-> ~S~%" current)))
      (values))