Sự khác biệt giữa "set", "setq" và "setf" trong Common Lisp là gì?
Sự khác biệt giữa "set", "setq" và "setf" trong Common Lisp là gì?
Câu trả lời:
Ban đầu, ở Lisp, không có biến từ vựng - chỉ có biến động. Và không có SETQ hay SETF, chỉ có chức năng SET.
Những gì bây giờ được viết là:
(setf (symbol-value '*foo*) 42)
được viết là:
(set (quote *foo*) 42)
mà cuối cùng đã được chuyển thành SETQ (SET trích dẫn):
(setq *foo* 42)
Sau đó, các biến từ vựng đã xảy ra và SETQ cũng được sử dụng để gán cho chúng - vì vậy nó không còn là một trình bao bọc đơn giản xung quanh SET.
Sau đó, ai đó đã phát minh ra SETF (Trường SET) như một cách chung để gán giá trị cho cấu trúc dữ liệu, để phản ánh giá trị l của các ngôn ngữ khác:
x.car := 42;
sẽ được viết là
(setf (car x) 42)
Đối với tính đối xứng và tổng quát, SETF cũng cung cấp chức năng của SETQ. Tại thời điểm này, sẽ đúng khi nói rằng SETQ là một nguyên thủy cấp thấp và SETF là một hoạt động cấp cao.
Sau đó, macro biểu tượng đã xảy ra. Vì vậy, các macro biểu tượng có thể hoạt động trong suốt, người ta nhận ra rằng SETQ sẽ phải hoạt động giống như SETF nếu "biến" được gán cho thực sự là một macro biểu tượng:
(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))
foo => 42
(setq foo 13)
foo => 13
*hidden* => (13 . 42)
Vì vậy, chúng tôi đến vào thời điểm hiện tại: SET và SETQ vẫn còn thiếu sót của các phương ngữ cũ và có thể sẽ được khởi động từ những người kế vị cuối cùng của Common Lisp.
f
thực tế là viết tắt của hàm , không phải trường (hoặc biểu mẫu , cho vấn đề đó) và cung cấp các tham chiếu, vì vậy trong khi setf cho trường có ý nghĩa nào đó, có vẻ như nó không chính xác.
set
là một hàm. Do đó, nó không biết môi trường. set
không thể thấy biến từ vựng. Nó chỉ có thể đặt giá trị ký hiệu của đối số của nó. setq
không được "đặt trích dẫn" nữa. Thực tế đó setq
là một hình thức đặc biệt, không phải là một vĩ mô cho thấy điều đó.
(set ls '(1 2 3 4)) => Error - ls has no value
(set 'ls '(1 2 3 4)) => OK
(setq ls '(1 2 3 4)) => OK - make ls to (quote ls) and then have the usual set
(setf ls '(1 2 3 4)) => OK - same as setq so far BUT
(setf (car ls) 10) => Makes ls '(10 2 3 4) - not duplicated by setq/set
(setq ls '(((1))))
, (setf (car (car (car ls))) 5)
là hành vi không xác định, bởi vì giá trị của ls
là không đổi (như sửa đổi một chuỗi ký tự bằng chữ C). Sau đó (setq ls (list (list (list 1))))
, (setf (car (car (car ls))) 5)
hoạt động giống như ls->val->val->val = 5
trong C.
setq
giống như set
với một đối số đầu tiên được trích dẫn - (set 'foo '(bar baz))
cũng giống như vậy (setq foo '(bar baz))
.setf
, mặt khác, thực sự tinh tế - nó giống như một "sự gián tiếp". Tôi đề nghị http://www.nano.com/lisp/cmucl-tutorials/LISP-tutorial-16.html như là một cách tốt hơn để bắt đầu tìm hiểu nó hơn bất kỳ câu trả lời ở đây có thể cung cấp cho ... trong ngắn hạn, tuy nhiên, setf
có những đối số đầu tiên là "tham chiếu", do đó, ví dụ như (aref myarray 3)
sẽ hoạt động (như đối số đầu tiên setf
) để đặt một mục bên trong một mảng.
Bạn có thể sử dụng setf
thay thế set
hoặc setq
không phải ngược lại kể từ khisetf
cũng có thể đặt giá trị của các phần tử riêng lẻ của một biến nếu biến có các phần tử riêng lẻ. Xem các ví dụ dưới đây:
Tất cả bốn ví dụ sẽ gán danh sách (1, 2, 3) cho biến có tên foo.
(set (quote foo) (list 1 2 3)) ;foo => (1 2 3)
(1 2 3)
(set 'foo '(1 2 3)) ;foo => (1 2 3) same function, simpler expression
(1 2 3)
(setq foo '(1 2 3)) ;foo => (1 2 3) similar function, different syntax
(1 2 3)
(setf foo '(1 2 3)) ;foo => (1 2 3) more capable function
(1 2 3)
setf
có thêm khả năng thiết lập một thành viên của danh sách foo
thành một giá trị mới.
foo ;foo => (1 2 3) as defined above
(1 2 3)
(car foo) ;the first item in foo is 1
1
(setf (car foo) 4) ;set or setq will fail since (car foo) is not a symbol
4
foo ;the fist item in foo was set to 4 by setf
(4 2 3)
Tuy nhiên, bạn có thể xác định một macro biểu tượng ghi lại một mục duy nhất trong foo
(define-symbol-macro foo-car (car foo)) ; assumes FOO => (1 2 3)
FOO-CAR
foo-car ;foo-car is now a symbol for the 1st item in foo
1
(setq foo-car 4) ;set or setq can set the symbol foo-car
4
foo ;Lisp macros are so cool
(4 2 3)
Bạn có thể sử dụng defvar
nếu bạn chưa xác định biến và không muốn cung cấp cho nó một giá trị cho đến sau này trong mã của bạn.
(defvar foo2)
(define-symbol-macro foo-car (car foo2))
Người ta có thể nghĩ SET
và SETQ
là các công trình cấp thấp.
SET
có thể đặt giá trị của các ký hiệu.
SETQ
có thể đặt giá trị của các biến.
Sau đó, SETF
là một macro, cung cấp nhiều loại thiết lập: biểu tượng, biến, phần tử mảng, vị trí thể hiện, ...
Đối với các ký hiệu và biến, người ta có thể nghĩ như thể SETF
mở rộng thành SET
và SETQ
.
* (macroexpand '(setf (symbol-value 'a) 10))
(SET 'A 10)
* (macroexpand '(setf a 10))
(SETQ A 10)
Vì vậy, SET
và SETQ
được sử dụng để thực hiện một số chức năng của SETF
, đó là cấu trúc tổng quát hơn. Một số câu trả lời khác cho bạn biết câu chuyện phức tạp hơn một chút, khi chúng ta tính đến các macro biểu tượng.
Tôi muốn thêm vào các câu trả lời trước đó rằng setf là macro gọi hàm cụ thể tùy thuộc vào nội dung được truyền làm đối số đầu tiên. So sánh kết quả mở rộng macro của setf với các loại đối số khác nhau:
(macroexpand '(setf a 1))
(macroexpand '(setf (car (list 3 2 1)) 1))
(macroexpand '(setf (aref #(3 2 1) 0) 1))
Đối với một số loại đối số, "hàm setf" sẽ được gọi:
(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))