2011-09-01 18 views
10

He escrito el siguiente fragmento de código para simular rodar una de seis caras morir varias veces y contar cuántas veces cada lado aterrizó:¿Qué está pasando con este código 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)))) 

La función funciona muy bien la primera vez que lo llamo , pero en llamadas posteriores, la variable myList comienza en el valor que tenía al final de la última llamada, en lugar de volver a inicializarse en ceros, como creía que debería ocurrir con la instrucción let. ¿Por qué pasó esto?

+1

Para su información, la mayoría de las personas que escriben el ceceo común actualmente usan nombres como 'mi-lista' en lugar de' miLista'. Además, no creo que necesite ese progn porque "el cuerpo de una let es un prognóstico implícito" http://www.lispworks.com/documentation/HyperSpec/Body/s_let_l.htm (es como si el progn ya estuviera allí)) –

Respuesta

13

Esto es resultado del uso de una lista constante en el inicializador:

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

Cambio esa línea para:

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

y se comportará como se espera. La primera línea solo da como resultado una asignación una vez (dado que es una lista constante), pero al llamar al list, fuerza la asignación para que ocurra cada vez que se ingresa la función.

editar: Esto puede ser útil, especialmente hacia el final. Successful Lisp

La respuesta a la pregunta this también puede ser útil.

Este utiliza la palabra clave loopcollecting que recoge los resultados de cada iteración en una lista y devuelve la lista como el valor de la loop.

+0

Gracias Andrew y Michael. Eso hizo el truco. Soy nuevo en Lisp (probablemente sea obvio) y revisaré el texto al que se vinculó. – Dustin

+0

No hay problema, Lisp es un lenguaje muy divertido de aprender :) Como Michael señaló que SBCL puede ser muy útil, es muy agresivo con la advertencia de operaciones posiblemente inseguras. – asm

+1

Además, normalmente, usar una matriz en lugar de una lista cuando intenta indexarla varias veces por posición y no crecer con frecuencia es una buena cosa. – Vatine

8

SBCL te dice lo que está mal:

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

Así que en esencia: No llame a funciones destructivas (aquí setf) en los datos constantes.

-1

como la publicación anterior, el compilador asigna el 0 como espacio constante. Solía ​​conocer algunos trucos para esto, uno sería lo convierten en un macro como:

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

o envuelto en una (eval-when (compile)) ...)

también

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

No sé si esto todavía funciona (o alguna vez funcionó) También hay algunas macros de de implementación o macros de compilación que podrían ayudar a mantener constante el tamaño de aloacción pero la variable de datos . No vuelvas a recordar de mi cabeza.

recuerde utilizar relleno (como bzero en c).