Perbedaan antara `set`,` setq`, dan `setf` di Common Lisp?

166

Apa perbedaan antara "set", "setq", dan "setf" di Common Lisp?

Richard Hoskins
sumber
9
Perilaku ini dijawab dengan cukup baik dalam jawaban, tetapi jawaban yang diterima mungkin memiliki etimologi yang keliru untuk "f" dalam "setf". Jawaban untuk Apa f dalam setf berdiri untuk? mengatakan bahwa ini untuk "fungsi", dan menyediakan referensi untuk mendukungnya.
Joshua Taylor

Jawaban:

169

Awalnya, di Lisp, tidak ada variabel leksikal - hanya variabel dinamis. Dan tidak ada SETQ atau SETF, hanya fungsi SET.

Apa yang sekarang ditulis sebagai:

(setf (symbol-value '*foo*) 42)

ditulis sebagai:

(set (quote *foo*) 42)

yang akhirnya disingkat menjadi SETQ (SET Dikutip):

(setq *foo* 42)

Kemudian variabel leksikal terjadi, dan SETQ menjadi digunakan untuk penugasan kepada mereka juga - jadi itu bukan lagi pembungkus sederhana di sekitar SET.

Kemudian, seseorang menemukan SETF (SET Field) sebagai cara umum untuk menetapkan nilai ke struktur data, untuk mencerminkan nilai-l bahasa lainnya:

x.car := 42;

akan ditulis sebagai

(setf (car x) 42)

Untuk simetri dan generalisasi, SETF juga menyediakan fungsionalitas SETQ. Pada titik ini, sudah benar untuk mengatakan bahwa SETQ adalah primitif tingkat rendah, dan SETF operasi tingkat tinggi.

Kemudian makro simbol terjadi. Agar makro simbol dapat bekerja secara transparan, disadari bahwa SETQ harus bertindak seperti SETF jika "variabel" yang ditugaskan adalah makro simbol:

(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))

foo => 42

(setq foo 13)

foo => 13

*hidden* => (13 . 42)

Jadi kita tiba di hari ini: SET dan SETQ adalah sisa-sisa dialek yang lebih tua, dan mungkin akan di-boot dari penerus Common Lisp.

stack programmer
sumber
45
Lisp umum selalu memiliki variabel leksikal. Anda harus berbicara tentang beberapa Lisp sebelum Common Lisp.
Rainer Joswig
4
Jika SET dan SETQ ingin di-boot dari penerus Common Lisp, mereka harus mendapatkan beberapa penggantian. Penggunaannya dalam kode tingkat tinggi terbatas, tetapi kode tingkat rendah (misalnya, kode SETF diterapkan) membutuhkannya.
Svante
13
apakah ada alasan Anda memilih 'mobil' sebagai bidang alih-alih sesuatu yang mungkin membingungkan sebagai fungsi mobil?
drudru
9
Ini jawaban untuk Apa f dalam setf berdiri untuk? mengklaim bahwa fsebenarnya singkatan dari function , bukan field (atau form , dalam hal ini), dan memberikan referensi, jadi sementara setf untuk field masuk akal, sepertinya itu mungkin tidak benar.
Joshua Taylor
1
Ringkasan: setadalah fungsi. Dengan demikian tidak mengenal lingkungan. settidak dapat melihat variabel leksikal. Ia hanya dapat mengatur nilai simbol dari argumennya. setqtidak "diatur dikutip" lagi. Fakta bahwa itu setqadalah bentuk khusus, bukan makro menunjukkan itu.
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
Sourav
sumber
12
Saya menemukan jawaban Anda lebih jelas daripada jawaban teratas. Terima kasih banyak.
CDR
2
@Sourav, tolong JANGAN PERNAH menggunakan huruf "l" (ell) sebagai variabel atau simbol dalam kode contoh. Terlalu sulit untuk membedakan secara visual dari angka 1.
DavidBooth
Tidak, saya masih tidak mengerti bagaimana (mobil ls) bisa bernilai l atau tidak. Apakah Anda mengerti bagaimana menerjemahkan CLisp ke dalam C? dan bagaimana cara menulis interpreter CLisp?
Reun
@ user1952009 clisp adalah salah satu implementasi Common Lisp. Jika Anda ingin merujuk ke bahasa itu sendiri, CL singkatan yang paling sering digunakan.
ssice
2
@ user1952009 Setelah (setq ls '(((1)))), (setf (car (car (car ls))) 5)adalah perilaku tidak terdefinisi, karena nilai lskonstan (seperti memodifikasi string literal dalam C). Setelah itu (setq ls (list (list (list 1)))), (setf (car (car (car ls))) 5)bekerja seperti ls->val->val->val = 5di C.
kyle
21

setqhanya seperti setdengan argumen pertama yang dikutip - (set 'foo '(bar baz))sama seperti (setq foo '(bar baz)). setf, di sisi lain, memang halus - itu seperti "tipuan". Saya menyarankan http://www.nano.com/lisp/cmucl-tutorials/LISP-tutorial-16.html sebagai cara yang lebih baik untuk memulai memahaminya daripada jawaban apa pun di sini yang dapat memberikan ... singkatnya, bagaimanapun, setfmengambil argumen pertama sebagai "referensi", sehingga mis (aref myarray 3)akan bekerja (sebagai argumen pertama untuk setf) untuk mengatur item di dalam array.

Alex Martelli
sumber
1
Masuk akal untuk nama setq. Mudah diingat. Terima kasih.
CDR
17

Anda dapat menggunakan setfsebagai pengganti setatau setqtidak sebaliknya karena setfjuga dapat mengatur nilai elemen individual dari suatu variabel jika variabel tersebut memiliki elemen individual. Lihat contoh di bawah ini:

Keempat contoh akan menetapkan daftar (1, 2, 3) ke variabel bernama 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)

setfmemiliki kemampuan tambahan untuk mengatur anggota daftar fooke nilai baru.

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)

Namun, Anda bisa mendefinisikan makro simbol yang merepresentasikan satu item di dalamnya 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)

Anda dapat menggunakan defvarjika Anda belum mendefinisikan variabel dan tidak ingin memberikan nilai sampai nanti dalam kode Anda.

(defvar foo2)
(define-symbol-macro foo-car (car foo2))
dansalmo
sumber
13

Orang dapat memikirkan SETdan SETQmenjadi konstruksi tingkat rendah.

  • SET dapat mengatur nilai simbol.

  • SETQ dapat mengatur nilai variabel.

Kemudian SETFadalah makro, yang menyediakan berbagai macam pengaturan hal: simbol, variabel, elemen array, slot contoh, ...

Untuk simbol dan variabel orang dapat berpikir seolah-olah SETFmengembang ke SETdan SETQ.

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

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

Jadi SETdan SETQdigunakan untuk mengimplementasikan beberapa fungsi SETF, yang merupakan konstruksi yang lebih umum. Beberapa jawaban lain memberi tahu Anda kisah yang sedikit lebih rumit, ketika kita memperhitungkan makro simbol.

Rainer Joswig
sumber
4

Saya ingin menambahkan jawaban sebelumnya bahwa setf adalah makro yang memanggil fungsi tertentu tergantung pada apa yang diteruskan sebagai argumen pertama. Bandingkan hasil ekspansi makro setf dengan berbagai jenis argumen:

(macroexpand '(setf a 1))

(macroexpand '(setf (car (list 3 2 1)) 1))

(macroexpand '(setf (aref #(3 2 1) 0) 1))

Untuk beberapa jenis argumen "fungsi setf" akan dipanggil:

(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))
Filipp
sumber