2012-12-26 26 views
6

Tôi biết rằng khi bạn muốn tạo một liên kết động/toàn cục trong Lisp, bạn sử dụng hoặc là defparameter hoặc defvar. Tôi cũng biết rằng bạn có thể thực hiện các ràng buộc từ vựng, tốt, hầu như ở khắp mọi nơi, bằng cách sử dụng danh sách đối số defun hoặc báo cáo.Điều gì xảy ra khi tôi sử dụng câu lệnh setf đơn giản trong LISP?

Những gì tôi đang tự hỏi là chính xác những gì nó tôi được thực hiện khi tôi thực hiện một tuyên bố như thế này trong đó x đã không tuyên bố hoặc sử dụng bất cứ nơi nào khác trong các mã:

(setf x 10) 

Điều này dường như làm việc tốt, và x dường như không hoạt động như một biến từ vựng. Nó thực sự là một toàn cầu năng động, giống như tôi đã sử dụng defparameter hoặc defvar, hoặc là nó cái gì khác hoàn toàn?

Trả lời

6

gì nó thực sự làm là không xác định trong tiêu chuẩn ANSI Common Lisp.

Nói chung, tôi thích bất kỳ cài đặt CL nào hơn để chỉ đặt giá trị được liên kết động hoặc giá trị toàn cầu. Nó không nên làm bất cứ điều gì khác. CMUCL theo mặc định dường như nghĩ rằng nó là một ý tưởng tốt để tuyên bố các biểu tượng đặc biệt sau đó. Nhưng đó là một ý tưởng tồi, vì không có cách nào rõ ràng để loại bỏ một tuyên bố đặc biệt toàn cầu.

Vì vậy, thường tôi mong chờ một cái gì đó như thế này (ở đây, LispWorks):

CL-USER 66 > (defun foo() (setf x44 10)) 
FOO 

Biến toàn cầu vẫn còn ràng buộc:

CL-USER 67 > x44 

Error: The variable X44 is unbound. 
    1 (continue) Try evaluating X44 again. 
    2 Specify a value to use this time instead of evaluating X44. 
    3 Specify a value to set X44 to. 
    4 (abort) Return to level 0. 
    5 Return to top loop level 0. 

Type :b for backtrace or :c <option number> to proceed. 
Type :bug-form "<subject>" for a bug report template or :? for other options. 

CL-USER 68 : 1 > :top 

Hãy gọi hàm:

CL-USER 69 > (foo) 
10 

Bây giờ, nó có giá trị toàn cầu:

CL-USER 70 > x44 
10 

Nhưng biến không được khai báo là đặc biệt (vì nó sẽ là DEFVAR hoặc DEFPARAMETER). Đây là một ràng buộc từ vựng được thiết lập.

CL-USER 71 > (let ((x44 20)) (foo) x44) 
20 

Khi chúng ta khai báo biến cục bộ là đặc biệt, sau đó chức năng của chúng tôi thay đổi các ràng buộc:

CL-USER 72 > (let ((x44 20)) (declare (special x44)) (foo) x44) 
10 
+1

Cảm ơn! Câu trả lời của bạn thực sự hữu ích. Sử dụng câu trả lời của bạn cùng với một số thử nghiệm trong REPL cuối cùng đã giúp tôi hiểu đầy đủ về hành vi sẽ xảy ra với tuyên bố ban đầu của tôi. Và bây giờ tôi cũng biết sự khác biệt giữa một biến đặc biệt và không đặc biệt. – Gearov

2

Rất lâu, bạn có thể nghĩ setq, đó là việc mở rộng setf bạn đã quan sát, như làm chỉ có một nửa của những gì defvar hoặc defparameter làm:

Hãy xem xét rằng defparameter thực hiện điều này:

(declaim (special x)) 
(setq x 10) 

Tức là cả hai đều cung cấp một số siêu dữ liệu (dữ liệu về loại điều x là) cho trình biên dịch (trong trường hợp này nó cho biết rằng đó là biến "đặc biệt") và gán giá trị.

Cụ thể, defvar sẽ không hoạt động như thế này, nếu đó là biểu mẫu cấp cao nhất. Các hành vi giữa các ý kiến ​​để khởi tạo giá trị cell của biểu tượng một lần duy nhất, do đó, mã của nó sẽ còn phức tạp hơn, một cái gì đó mà bạn có thể nghĩ ra như:

(unless (boundp x) ; This is not entirely correct, because if the symbol 
         ; is otherwise known to the environment, but is unbound 
         ; defvar will not re-bind it, but I can't think of a way 
         ; to mimic that behavior 
    (declaim (special x)) 
    (setq x 10)) 

các siêu dữ liệu cung cấp cho trình biên dịch có thể hoặc có thể không có bất kỳ ảnh hưởng nào đến cách mã sẽ hoạt động. Nói chung, siêu dữ liệu được cho là hỗ trợ trình biên dịch để đưa ra phán đoán tốt hơn về ý định đằng sau mã của bạn và do đó có thể dẫn đến tối ưu hóa. Nhưng nó cũng có thể hữu ích cho tài liệu hoặc gỡ lỗi.

Bạn có thể đọc khoảng specialdeclare trong Hyperspec.

0

Theo tôi biết bạn nên tránh sử dụng setf (mở rộng thành setq) với các biến chưa khai báo. Hành vi có thể khác nhau giữa việc triển khai và ngay cả khi mã của bạn trong REPL hoạt động tốt, chương trình được biên dịch có thể bị lỗi ở vị trí không mong muốn. Bạn có thể thấy cảnh báo trong CLISP nếu bạn làm điều gì đó như thế này:

>(defun internal-setf() (setf some-var 10)) 
>(compile 'internal-setf) 
WARNING: in INTERNAL-SETF : SOME-VAR is neither declared nor bound, 
    it will be treated as if it were declared SPECIAL. 
Các vấn đề liên quan