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

Dus*_*tin 10 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))))
Run Code Online (Sandbox Code Playgroud)

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

asm*_*asm 13

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

(let ((myList '(0 0 0 0 0 0)))
Run Code Online (Sandbox Code Playgroud)

将该行更改为:

(let ((myList (list 0 0 0 0 0 0)))
Run Code Online (Sandbox Code Playgroud)

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

编辑:这可能会有所帮助,特别是在最后. 成功的Lisp

这个问题的答案也可能有所帮助.

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


Mic*_*ert 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
Run Code Online (Sandbox Code Playgroud)

所以本质上:不要setf在常量数据上调用破坏性函数(这里).