为什么词法变量保留价值?

时间:2019-02-13 20:47:35

标签: 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)

输出:

temp: (NIL)
temp: ((5))

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

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

(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))

但是这个有效:

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

这也是可行的:

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

(my-test)

这也是:

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

(my-test)

但不是这个:

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

(my-test)

  • 为什么它对某些代码有效,而对另一些代码无效?
  • 如何在多个调用之间保留词法值?

1 个答案:

答案 0 :(得分:5)

Common Lisp规范没有指定当您尝试变异常量数据时会发生什么。常量数据为:

  1. quote运算符产生的数据
  2. 任何能够自我评估并且是源代码一部分的对象

这样做的目的是允许实现使用只读存储器(无需gc对其进行扫描)来获取常量,并允许对相同的常量重新使用存储。

代码如下:

(defun foo ()
  ... '(()) ...)

可以转换为:

(defconstant +foo1+ '(()))
(defun foo ()
  ... +foo1+ ...)

不偏离标准的文字或精神。