为什么词汇变量保留了价值?

Fni*_*fni 1 common-lisp

我不明白以下代码:

(defun my-test ()
  (let ((temp '(())))
    (format t "temp: ~a~%" temp)
    ((lambda ()
       (push 5 (first temp))))))

;; Execute this call twice in a row.
(my-test)
Run Code Online (Sandbox Code Playgroud)

输出:

temp: (NIL)
temp: ((5))
Run Code Online (Sandbox Code Playgroud)

如何temp保存价值?我知道有以下警告,但我不明白这种行为背后的逻辑.

; in: DEFUN MY-TEST
;     (PUSH 5 (FIRST TEMP))
; --> LET* 
; ==>
;   (SB-KERNEL:%RPLACA #:TEMP0 (CONS 5 (FIRST #:TEMP0)))
; 
; caught WARNING:
;   Destructive function SB-KERNEL:%RPLACA called on constant data: (NIL)
;   See also:
;     The ANSI Standard, Special Operator QUOTE
;     The ANSI Standard, Section 3.2.2.3
; 
; compilation unit finished
;   caught 1 WARNING condition
Run Code Online (Sandbox Code Playgroud)

以下代码输出相同的结果:

(flet ((my-fct ()
     (let ((temp '(())))
       (format t "temp: ~a~%" temp)
       ((lambda ()
          (push 5 (first temp)))))))
  (my-fct)
  (my-fct))

(let ((fct (lambda ()
         (let ((temp '(())))
           (format t "temp: ~a~%" temp)
           ((lambda ()
          (push 5 (first temp))))))))
  (funcall fct)
  (funcall fct))
Run Code Online (Sandbox Code Playgroud)

但是这个有效:

;; Execute this call twice in a row.
((lambda ()
   (let ((temp '(())))
     (format t "temp: ~a~%" temp)
     ((lambda ()
    (push 5 (first temp)))))))
Run Code Online (Sandbox Code Playgroud)

这个也有效:

(defun my-test ()
  (let ((temp (list ())))
    (format t "temp: ~a~%" temp)
    ((lambda ()
       (push 5 (first temp))))))

(my-test)
Run Code Online (Sandbox Code Playgroud)

还有这个:

(defun my-test ()
  (let ((temp (list (list))))
    (format t "temp: ~a~%" temp)
    ((lambda ()
       (push 5 (first temp))))))

(my-test)
Run Code Online (Sandbox Code Playgroud)

但不是这个:

(defun my-test ()
  (let ((temp `(,(list))))
    (format t "temp: ~a~%" temp)
    ((lambda ()
       (push 5 (first temp))))))

(my-test)
Run Code Online (Sandbox Code Playgroud)
  • 为什么它适用于某些代码,而不适用于其他代码?
  • 如何在多个调用中保留词汇值?

Dan*_*son 5

Common Lisp规范没有规定当您尝试改变常量数据时会发生什么.常数数据是:

  1. quote运营商生成的数据
  2. 任何评估自身并且是源代码一部分的对象

这样做的目的是允许实现使用只读存储器(不需要由gc扫描)来获取常量,并将存储重用于相等的常量.

所以代码:

(defun foo ()
  ... '(()) ...)
Run Code Online (Sandbox Code Playgroud)

可以转换为:

(defconstant +foo1+ '(()))
(defun foo ()
  ... +foo1+ ...)
Run Code Online (Sandbox Code Playgroud)

不偏离标准的字母或精神.