这个Common Lisp代码发生了什么?

时间:2011-09-01 20:39:02

标签: lisp common-lisp

我编写了下面的代码来模拟多次掷六面骰子并计算每一方落下的次数:

(defun dice (num)
  (let ((myList '(0 0 0 0 0 0)))
    (progn (format t "~a" myList)
           (loop for i from 1 to num do
                 (let ((myRand (random 6)))
                   (setf (nth myRand myList) (+ 1 (nth myRand myList)))))
           (format t "~a" myList))))

该函数在我第一次调用它时效果很好,但在后续调用中,变量myList以最后一次调用结束时的值开始,而不是像我认为应该发生的那样初始化为全零。 let 语句。为什么会这样?

3 个答案:

答案 0 :(得分:13)

这是在初始化程序中使用常量列表的结果:

(let ((myList '(0 0 0 0 0 0)))

将该行更改为:

(let ((myList (list 0 0 0 0 0 0)))

它会表现得像你期望的那样。第一行只导致一次分配(因为它是一个常量列表),但是通过调用list,每次输入函数时都会强制进行分配。

编辑: 这可能会有所帮助,特别是在最后。 Successful Lisp

this问题的答案也可能有所帮助。

这使用loop关键字collecting将每次迭代的结果收集到列表中,并将列表作为loop的值返回。

答案 1 :(得分:8)

SBCL告诉你什么是错的:

* (defun dice (num)
  (let ((myList '(0 0 0 0 0 0)))
    (progn (format t "~a" myList)
           (loop for i from 1 to num do
                 (let ((myRand (random 6)))
                   (setf (nth myRand myList) (+ 1 (nth myRand myList)))))
           (format t "~a" myList))))
; in: DEFUN DICE
;     (SETF (NTH MYRAND MYLIST) (+ 1 (NTH MYRAND MYLIST)))
; ==>
;   (SB-KERNEL:%SETNTH MYRAND MYLIST (+ 1 (NTH MYRAND MYLIST)))
; 
; caught WARNING:
;   Destructive function SB-KERNEL:%SETNTH called on constant data.
;   See also:
;     The ANSI Standard, Special Operator QUOTE
;     The ANSI Standard, Section 3.2.2.3
; 
; compilation unit finished
;   caught 1 WARNING condition

DICE

所以本质上:不要在常数数据上调用破坏性函数(此处setf)。

答案 2 :(得分:-1)

与上面的帖子一样,编译器将0分配为常量空间。我曾经知道这方面的一些技巧,一个会使它成为一个宏:

`',(list 0 0 0 0 0)
=>
 ?? (I forget and don't have the other machine on to check)

或包裹在(eval-when (compile)) ... )

 (list 0 0 0 0 0) 
=>
  #.(list 0 0 0 0)

我不知道这是否仍然有效(或曾经有效)。这也是一些实施 宏或编译器宏,可以帮助保持alloaction大小不变但数据 变量。不要再记住我的头顶了。

记得使用填充(如c中的bzero)。