2011-09-01 41 views
10

Tôi đã viết các bit mã sau đây để mô phỏng lăn sáu mặt chết một số lần và đếm bao nhiêu lần mỗi bên hạ cánh lên:Điều gì đang xảy ra với mã Lisp chung này?

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

Chức năng hoạt động tuyệt vời lần đầu tiên tôi gọi nó là , nhưng trên các cuộc gọi tiếp theo, biến myList bắt đầu với giá trị mà nó có vào cuối cuộc gọi cuối cùng, thay vì được khởi tạo trở lại tất cả các số 0 như tôi nghĩ sẽ xảy ra với câu lệnh để cho câu hỏi. Lý do tại sao điều này xảy ra?

+1

FYI, hầu hết mọi người viết lisp chung những ngày này sử dụng tên như 'my-list' thay vì' myList'. Ngoài ra, tôi không tin rằng bạn cần progn đó bởi vì "cơ thể của một let là một progn ngầm" http://www.lispworks.com/documentation/HyperSpec/Body/s_let_l.htm (nó giống như progn đã có). –

Trả lời

13

Đây là kết quả của việc sử dụng một danh sách liên tục trong initializer:

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

Change lót để:

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

và nó sẽ cư xử như bạn mong đợi. Dòng đầu tiên chỉ dẫn đến phân bổ một lần (vì đó là danh sách không đổi), nhưng bằng cách gọi list bạn buộc phân bổ xảy ra mỗi khi hàm được nhập.

chỉnh sửa: Điều này có thể hữu ích, đặc biệt là khi kết thúc. Successful Lisp

Câu trả lời cho câu hỏi this cũng có thể hữu ích.

Điều này sử dụng loop từ khóa collecting để thu thập kết quả của mỗi lần lặp vào danh sách và trả về danh sách dưới dạng giá trị của loop.

+0

Cảm ơn Andrew và Michael. Điều đó đã làm các trick. Tôi mới với Lisp (đó có lẽ là hiển nhiên) và tôi sẽ kiểm tra các văn bản mà bạn liên kết đến. – Dustin

+0

Không có vấn đề gì, Lisp là một ngôn ngữ rất thú vị để học :) Như Michael đã chỉ ra SBCL có thể rất hữu ích, nó rất tích cực về cảnh báo về các hoạt động có thể không an toàn. – asm

+1

Ngoài ra, thông thường, sử dụng một mảng thay vì một danh sách khi bạn có ý định liên tục lập chỉ mục nó theo vị trí và không thường xuyên phát triển nó là một điều tốt. – Vatine

8

SBCL cho bạn biết những gì là sai:

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

Vì vậy, trong bản chất: Đừng gọi chức năng phá hoại (ở đây setf) trên dữ liệu liên tục.

-1

như bài đăng ở trên, trình biên dịch phân bổ 0 là không gian cố định. Tôi đã từng biết một số thủ thuật cho điều này, người ta sẽ được làm cho nó một vĩ mô như:

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

hoặc bọc trong một (eval-when (compile)) ...)

cũng

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

Tôi không biết nếu vẫn còn đây công trình (hoặc đã từng làm việc). Ngoài ra, một số macro macro hoặc macro biên dịch có thể giúp giữ nguyên kích thước alloaction nhưng biến số . Đừng nhớ đến đỉnh đầu của tôi nữa.

nhớ để sử dụng điền (như bzero trong c).

Các vấn đề liên quan