Sự khác biệt giữa `set`,` setq` và` setf` trong Common Lisp?


166

Sự khác biệt giữa "set", "setq" và "setf" trong Common Lisp là gì?


9
Hành vi của những điều này được trả lời khá tốt trong các câu trả lời, nhưng câu trả lời được chấp nhận có từ nguyên có thể bị nhầm với "f" trong "setf". Câu trả lời cho f trong setf là gì? nói rằng đó là "chức năng" và cung cấp tài liệu tham khảo để sao lưu.
Joshua Taylor

Câu trả lời:


169

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.


45
Lisp thông thường luôn có các biến từ vựng. Bạn phải nói về một số Lisp trước Common Lisp.
Rainer Joswig

4
Nếu SET và SETQ được khởi động từ một người kế vị chung Lisp, họ sẽ phải có một số thay thế. Việc sử dụng chúng trong mã cấp cao bị hạn chế, nhưng mã cấp thấp (ví dụ: mã SETF được triển khai) cần chúng.
Svante

13
Có lý do gì bạn chọn 'xe hơi' làm trường thay vì thứ gì đó có thể bị nhầm lẫn là chức năng của xe không?
drudru

9
Câu trả lời này để Gì f trong setf đứng cho? tuyên bố rằng fthự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.
Joshua Taylor

1
Tóm tắt: setlà một hàm. Do đó, nó không biết môi trường. setkhô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ó. setqkhông được "đặt trích dẫn" nữa. Thực tế đó setqlà một hình thức đặc biệt, không phải là một vĩ mô cho thấy điều đó.
KIM Taegyoon

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

12
Tôi thấy câu trả lời của bạn rõ ràng hơn câu trả lời hàng đầu. Cảm ơn rất nhiều.
CDR

2
@Sourav, vui lòng KHÔNG BAO GIỜ sử dụng chữ "l" (ell) làm biến hoặc ký hiệu trong mã ví dụ. Thật quá khó để phân biệt trực quan với chữ số 1.
DavidBooth

Không, tôi vẫn không hiểu làm thế nào (xe ls) có thể là giá trị l hay không. Bạn có hiểu làm thế nào để dịch CLisp sang C? và làm thế nào để viết một trình thông dịch CLisp?
tái hợp

@ user1952009 clisp là một triển khai Lisp phổ biến. Nếu bạn muốn đề cập đến chính ngôn ngữ, CL viết tắt được sử dụng nhiều nhất.
ssice

2
@ user1952009 Sau (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 lslà 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 = 5trong C.
kyle

21

setqgiống như setvớ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, setfcó 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.


1
Làm cho ý nghĩa nhất cho tên setq. Dễ nhớ. Cảm ơn.
CDR

17

Bạn có thể sử dụng setfthay thế sethoặc setqkhô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)

setfcó thêm khả năng thiết lập một thành viên của danh sách foothà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 defvarnế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))

13

Người ta có thể nghĩ SETSETQlà 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 đó, SETFlà 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ể SETFmở rộng thành SETSETQ.

* (macroexpand '(setf (symbol-value 'a) 10))

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

Vì vậy, SETSETQđượ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.


4

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))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.