2012-01-22 37 views
15

Ai đó có thể giải thích hành vi sau đây không? Cụ thể, tại sao hàm trả về một danh sách khác mỗi lần? Tại sao không phải là some-list được khởi tạo thành '(0 0 0) mỗi lần hàm được gọi?Tại sao chức năng này trả về một giá trị khác nhau mỗi lần?

(defun foo() 
    (let ((some-list '(0 0 0))) 
    (incf (car some-list)) 
    some-list)) 

Output:

> (foo) 
(1 0 0) 
> (foo) 
(2 0 0) 
> (foo) 
(3 0 0) 
> (foo) 
(4 0 0) 

Cảm ơn!

EDIT:

Ngoài ra, những gì là cách khuyến khích thực hiện chức năng này, giả sử tôi muốn các chức năng để đầu ra '(1 0 0) mỗi lần?

Trả lời

21

'(0 0 0) là một đối tượng theo nghĩa đen, mà được giả định là một hằng số (mặc dù không được bảo vệ từ sửa đổi). Vì vậy, bạn có hiệu quả sửa đổi cùng một đối tượng mỗi lần. Để tạo các đối tượng khác nhau tại mỗi cuộc gọi chức năng, sử dụng (list 0 0 0).

Vì vậy, trừ khi bạn biết, bạn đang làm gì, bạn nên luôn sử dụng danh sách đen (như '(0 0 0)) chỉ làm hằng số.

+0

Ah, giờ đây có ý nghĩa. Cảm ơn lời giải thích rõ ràng. –

+2

Có lẽ nó sẽ là tốt đẹp để thêm rằng quasiquoting cũng không được đảm bảo để trở về danh sách mới. – 6502

+3

"trừ khi bạn biết, bạn đang làm gì" Hành vi sửa đổi dữ liệu chữ là không xác định. Theo spec, bạn có thể không thực sự biết những gì bạn đang làm (chắc chắn), vì vậy "bạn nên ** luôn luôn ** sử dụng danh sách đen (như '(0 0 0)) chỉ là hằng số". –

-5

Muốn viết một bản thân mình, nhưng tôi tìm thấy một tốt nhất trực tuyến:

CommonLisp có chức năng hạng nhất, ví dụ: chức năng là các đối tượng mà thể được tạo ra khi chạy, và thông qua như là đối số cho các chức năng khác. --AlainPicard Các hàm hạng nhất này cũng có trạng thái riêng của chúng, vì vậy chúng là các hàm. Tất cả các hàm Lisp là các hàm functors; không có cách ly giữa các hàm "chỉ mã" và "chức năng đối tượng". Trạng thái có dạng biến từ vựng được ghi . Bạn không cần phải sử dụng LAMBDA để nắm bắt các ràng buộc; một defun cấp cao nhất có thể làm điều đó quá: (let ((tư nhân biến 42)) (defun foo() ...))

Mã ở vị trí của ... thấy tin biến trong phạm vi lexical của nó. Có một ví dụ của biến này được liên kết với một đối tượng hàm và chỉ được gắn liền với biểu tượng FOO; Biến số được chụp tại thời điểm biểu thức DEFUN được đánh giá. Biến này sau đó hoạt động giống như biến tĩnh trong C. Hoặc, luân phiên, bạn có thể coi FOO là đối tượng "đơn lẻ" với biến số cá thể ". --KazKylheku

Ref http://c2.com/cgi/wiki?CommonLisp

+1

Bạn có thể giải thích cách văn bản bạn trích dẫn có liên quan đến câu hỏi không? Tôi có thể thiếu một cái gì đó, nhưng tôi không thấy nó. – sepp2k

+0

Văn bản giải thích cách các hàm là các đối tượng hạng nhất trong Lisp, và thực sự có một "trạng thái". Biến được khai báo là một phần của "trạng thái" của hàm. Như văn bản giải thích, điều này rất giống với việc khai báo các biến cục bộ tĩnh trong C. Phần nào của văn bản không liên quan đến vấn đề này? – xtrem

+2

Phần không phải là tất cả những gì đang xảy ra. Báo giá của bạn nói về "ràng buộc biến từ vựng". Tuy nhiên 'some-list' là một biến cục bộ của' foo', nó không phải là một biến được capture và do đó không phải là một phần của trạng thái 'foo'. Trên mỗi lệnh gọi 'foo',' some-list' sẽ có một ràng buộc duy nhất (như Vsevolod được giải thích sẽ trỏ đến cùng một danh sách "liên tục", giải thích hành vi của OP). Điều này hoàn toàn khác với một hàm sửa đổi các biến bị bắt. – sepp2k

9

Trên một mặt lưu ý, việc xác định chức năng này trong REPL sbcl bạn nhận được cảnh báo sau đây:

caught WARNING: 
    Destructive function SB-KERNEL:%RPLACA called on constant data. 
    See also: 
     The ANSI Standard, Special Operator QUOTE 
     The ANSI Standard, Section 3.2.2.3 

nào đưa ra một gợi ý tốt đối với vấn đề trong tầm tay.

4

'(0 0 0) trong mã là dữ liệu theo nghĩa đen. Sửa đổi dữ liệu này có hành vi không xác định. Việc triển khai Lisp thường gặp có thể không phát hiện được nó trong thời gian chạy (trừ khi dữ liệu được ví dụ được đặt trong một số không gian bộ nhớ chỉ đọc). Nhưng nó có thể có tác dụng không mong muốn.

  • bạn thấy rằng dữ liệu này có thể (và thường là) chia sẻ trên lời gọi khác nhau của cùng một chức năng

  • một trong các lỗi tinh tế hơn có thể là thế này: Common Lisp đã được xác định với tối ưu hóa khác nhau có thể được thực hiện bởi một trình biên dịch trong tâm trí. Ví dụ một trình biên dịch được phép tái sử dụng dữ liệu:

Ví dụ:

(let ((a '(1 2 3)) 
     (b '(1 2 3))) 
    (list a b)) 

Trong đoạn code trên đoạn mã trình biên dịch có thể phát hiện rằng các dữ liệu đen của abEQUAL. Sau đó nó có thể có cả hai biến trỏ đến cùng một dữ liệu theo nghĩa đen. Sửa đổi nó có thể hoạt động, nhưng thay đổi có thể nhìn thấy từ ab.

Tóm tắt: Sửa đổi dữ liệu theo nghĩa đen là nguồn gốc của một số lỗi tinh vi. Tránh nó nếu có thể. Sau đó, bạn cần phải cons các đối tượng dữ liệu mới. Tiêu thụ nói chung có nghĩa là phân bổ cấu trúc dữ liệu mới, mới khi chạy.

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