Setelah berhasil melewati bagian utama dari buku pengantar Lisp, saya masih tidak dapat memahami apa fungsi operator khusus (quote)
(atau setara '
), namun ini telah terjadi di seluruh kode Lisp yang pernah saya lihat.
Apa fungsinya?
Jawaban singkat Bypass aturan evaluasi standar dan melakukan tidak mengevaluasi ekspresi (simbol atau s-exp), lewat itu bersama ke fungsi persis seperti yang diketikkan.
Jawaban Panjang: Aturan Evaluasi Default
Ketika fungsi reguler (saya akan membahasnya nanti) dipanggil, semua argumen yang diteruskan ke sana dievaluasi. Artinya Anda bisa menulis ini:
(* (+ a 2)
3)
Yang pada gilirannya mengevaluasi (+ a 2)
, dengan mengevaluasi a
dan 2. Nilai simbol a
dicari di set pengikatan variabel saat ini, dan kemudian diganti. Say a
saat ini terikat dengan nilai 3:
(let ((a 3))
(* (+ a 2)
3))
Kita akan mendapatkan (+ 3 2)
, + kemudian dipanggil pada 3 dan 2 menghasilkan 5. Bentuk asli kita sekarang(* 5 3)
menghasilkan 15.
Menjelaskan quote
Sudah!
Baik. Seperti yang terlihat di atas, semua argumen ke suatu fungsi dievaluasi, jadi jika Anda ingin meneruskan simbol a
dan bukan nilainya, Anda tidak ingin mengevaluasinya. Simbol Lisp dapat berfungsi ganda sebagai nilainya, dan penanda di mana Anda dalam bahasa lain akan menggunakan string, seperti kunci untuk tabel hash.
Di sinilah quote
masuk. Katakanlah Anda ingin memplot alokasi sumber daya dari aplikasi Python, tetapi melakukan plotting di Lisp. Minta aplikasi Python Anda melakukan sesuatu seperti ini:
print("'(")
while allocating:
if random.random() > 0.5:
print(f"(allocate {random.randint(0, 20)})")
else:
print(f"(free {random.randint(0, 20)})")
...
print(")")
Memberi Anda keluaran yang terlihat seperti ini (sedikit cantik):
'((allocate 3)
(allocate 7)
(free 14)
(allocate 19)
...)
Ingat apa yang saya katakan tentang quote
("centang") yang menyebabkan aturan default tidak berlaku? Baik. Apa yang sebaliknya akan terjadi adalah bahwa nilai-nilai allocate
dan free
dijunjung tinggi, dan kita tidak menginginkan itu. Di Lisp kami, kami ingin melakukan:
(dolist (entry allocation-log)
(case (first entry)
(allocate (plot-allocation (second entry)))
(free (plot-free (second entry)))))
Untuk data yang diberikan di atas, urutan pemanggilan fungsi berikut akan dilakukan:
(plot-allocation 3)
(plot-allocation 7)
(plot-free 14)
(plot-allocation 19)
Tapi Tentang Apa list
?
Nah, terkadang Anda memang ingin mengevaluasi argumen. Katakanlah Anda memiliki fungsi bagus yang memanipulasi angka dan string dan mengembalikan daftar hasil ... hal. Mari kita membuat awal yang salah:
(defun mess-with (number string)
'(value-of-number (1+ number) something-with-string (length string)))
Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER (1+ NUMBER) SOMETHING-WITH-STRING (LENGTH STRING))
Hei! Bukan itu yang kami inginkan. Kami ingin mengevaluasi beberapa argumen secara selektif , dan membiarkan yang lain sebagai simbol. Coba # 2!
(defun mess-with (number string)
(list 'value-of-number (1+ number) 'something-with-string (length string)))
Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER 21 SOMETHING-WITH-STRING 3)
Bukan Hanya quote
, Tapibackquote
Jauh lebih baik! Kebetulan, pola ini sangat umum di (kebanyakan) makro, sehingga ada sintaks khusus untuk melakukan hal itu. Kutipan belakang:
(defun mess-with (number string)
`(value-of-number ,(1+ number) something-with-string ,(length string)))
Ini seperti menggunakan quote
, tetapi dengan opsi untuk mengevaluasi secara eksplisit beberapa argumen dengan mengawalnya dengan koma. Hasilnya sama dengan menggunakan list
, tetapi jika Anda membuat kode dari makro, Anda sering kali hanya ingin mengevaluasi sebagian kecil dari kode yang dikembalikan, sehingga kutipan balik lebih cocok. Untuk daftar yang lebih pendek, list
bisa lebih mudah dibaca.
Hei, Anda Lupa quote
!
Jadi, bagaimana ini meninggalkan kita? Oh benar, quote
sebenarnya apa yang dilakukannya? Ini hanya mengembalikan argumennya tidak dievaluasi! Ingat apa yang saya katakan di awal tentang fungsi reguler? Ternyata bahwa beberapa operator / fungsi perlu tidak mengevaluasi argumen mereka. Seperti IF - Anda tidak ingin cabang lain dievaluasi jika tidak diambil, bukan? Yang disebut operator khusus , bersama dengan makro, bekerja seperti itu. Operator khusus juga merupakan "aksioma" dari bahasa - seperangkat aturan minimal - di mana Anda dapat mengimplementasikan Lisp lainnya dengan menggabungkannya bersama dengan cara yang berbeda.
Kembali ke quote
:
Lisp> (quote spiffy-symbol)
SPIFFY-SYMBOL
Lisp> 'spiffy-symbol ; ' is just a shorthand ("reader macro"), as shown above
SPIFFY-SYMBOL
Bandingkan dengan (di Steel-Bank Common Lisp):
Lisp> spiffy-symbol
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "initial thread" RUNNING {A69F6A9}>:
The variable SPIFFY-SYMBOL is unbound.
Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(SB-INT:SIMPLE-EVAL-IN-LEXENV SPIFFY-SYMBOL #<NULL-LEXENV>)
0]
Karena tidak ada spiffy-symbol
dalam ruang lingkup saat ini!
Menyimpulkan
quote
, backquote
(dengan koma), dan list
adalah beberapa alat yang Anda gunakan untuk membuat daftar, yang tidak hanya daftar nilai, tetapi seperti yang Anda lihat dapat digunakan sebagai struct
struktur data yang ringan (tidak perlu mendefinisikan a )!
Jika Anda ingin mempelajari lebih lanjut, saya merekomendasikan buku Peter Seibel Practical Common Lisp untuk pendekatan praktis dalam mempelajari Lisp, jika Anda sudah menguasai pemrograman secara luas. Akhirnya dalam perjalanan Lisp Anda, Anda akan mulai menggunakan paket juga. Panduan The Idiot untuk Paket Common Lisp dari Ron Garret akan memberi Anda penjelasan yang baik tentang itu.
Selamat meretas!
this
, laluis
, lalutrue
, tetapi Anda hanya melihat yang terakhir dikembalikan. (ini dan benar adalah pernyataan terpisah)Dikatakan "jangan evaluasi saya". Misalnya, jika Anda ingin menggunakan daftar sebagai data, dan bukan sebagai kode, Anda akan meletakkan kutipan di depannya. Sebagai contoh,
(print '(+ 3 4))
mencetak "(+ 3 4)", sedangkan(print (+ 3 4))
mencetak "7"sumber
unquote
perintah?eval
:(print (eval '(+ 3 4)))
. Inilah yang membuat Lisps begitu hebat: daftar adalah kode, dan kode adalah daftar, sehingga program Lisp dapat memanipulasi dirinya sendiri.Orang lain telah menjawab pertanyaan ini dengan mengagumkan, dan Matthias Benkard memberikan peringatan yang sangat bagus.
JANGAN GUNAKAN QUOTE UNTUK MEMBUAT DAFTAR YANG AKAN ANDA MODIFIKASI NANTI. Spesifikasi memungkinkan compiler untuk memperlakukan daftar yang dikutip sebagai konstanta. Seringkali, kompilator akan mengoptimalkan konstanta dengan membuat satu nilai untuk konstanta tersebut di memori dan kemudian mereferensikan nilai tunggal tersebut dari semua lokasi tempat konstanta muncul. Dengan kata lain, ini mungkin memperlakukan konstanta seperti variabel global anonim.
Ini dapat menyebabkan masalah yang jelas. Jika Anda memodifikasi sebuah konstanta, itu mungkin sangat baik mengubah penggunaan lain dari konstanta yang sama dalam kode yang sama sekali tidak terkait. Misalnya, Anda dapat membandingkan beberapa variabel dengan '(1 1) di beberapa fungsi, dan di fungsi yang sama sekali berbeda, mulailah daftar dengan' (1 1) lalu tambahkan lebih banyak hal ke dalamnya. Saat menjalankan fungsi-fungsi ini, Anda mungkin menemukan bahwa fungsi pertama tidak lagi cocok dengan hal-hal yang benar, karena sekarang mencoba membandingkan variabel dengan '(1 1 2 3 5 8 13), yang merupakan hasil dari fungsi kedua. Kedua fungsi ini sama sekali tidak terkait, tetapi memiliki pengaruh satu sama lain karena penggunaan konstanta. Bahkan efek buruk yang lebih gila dapat terjadi, seperti iterasi daftar normal yang tiba-tiba berulang tanpa batas.
Gunakan kutipan saat Anda membutuhkan daftar konstan, seperti untuk perbandingan. Gunakan daftar ketika Anda akan mengubah hasilnya.
sumber
(list (+ 1 2))
sebagian besar waktu. Jika demikian, bagaimana Anda mencegah evaluasi(+ 1 2)
di dalam contoh seperti itu? Apakah adaunquote
perintah?'((3))
, atau yang setara dengan'((+ 1 2))
? Jika yang terakhir, Anda harus menggunakan lebihlist
:(list (list '+ 1 2))
. Atau jika Anda menginginkan yang setara'(+ 1 2)
, adil(list '+ 1 2)
. Dan ingat, jika Anda tidak mengubah daftar, silakan gunakan kutipan: tidak ada yang salah'(+ 1 2)
jika Anda hanya membandingkannya atau sesuatu.Satu jawaban untuk pertanyaan ini mengatakan bahwa QUOTE “membuat struktur data daftar”. Ini kurang tepat. QUOTE lebih mendasar dari ini. Faktanya, QUOTE adalah operator sepele: Tujuannya adalah untuk mencegah apa pun terjadi sama sekali. Secara khusus, itu tidak menciptakan apa pun.
Apa yang (QUOTE X) katakan pada dasarnya adalah "jangan lakukan apa-apa, beri aku X". X tidak perlu berupa daftar seperti di (QUOTE (ABC)) atau simbol seperti di (QUOTE FOO). Itu bisa menjadi objek apapun. Memang, hasil evaluasi list yang dihasilkan oleh (LIST 'QUOTE SOME-OBJECT) akan selalu mengembalikan BEBERAPA-OBJECT, apapun itu.
Sekarang, alasan mengapa (QUOTE (ABC)) tampak seolah-olah itu membuat daftar yang elemennya adalah A, B, dan C adalah bahwa daftar seperti itu benar-benar adalah apa yang dikembalikannya; tetapi pada saat formulir QUOTE dievaluasi, daftar tersebut umumnya sudah ada untuk sementara waktu (sebagai komponen dari formulir QUOTE!), dibuat oleh pemuat atau pembaca sebelum kode dijalankan.
Salah satu implikasi dari hal ini yang cenderung membuat pemula cukup sering tersandung adalah sangat tidak bijaksana untuk mengubah daftar yang dikembalikan oleh formulir QUOTE. Data yang dikembalikan oleh QUOTE, untuk semua maksud dan tujuan, dianggap sebagai bagian dari kode yang sedang dijalankan dan oleh karena itu harus diperlakukan sebagai hanya baca!
sumber
Kutipan mencegah eksekusi atau evaluasi formulir, malah mengubahnya menjadi data. Secara umum Anda dapat mengeksekusi data dengan mengevaluasinya.
kutipan membuat struktur data daftar, misalnya, berikut ini adalah setara:
Ini juga dapat digunakan untuk membuat daftar (atau pohon):
Anda mungkin lebih baik mendapatkan buku pengantar tentang cadel, seperti Practical Common Lisp (yang tersedia untuk dibaca on-line).
sumber
Di Emacs Lisp:
Apa yang bisa dikutip?
Daftar dan simbol.
Mengutip bilangan berarti mengevaluasi bilangan itu sendiri:
'5
sama dengan5
.Apa yang terjadi saat Anda mengutip daftar?
Sebagai contoh:
'(one two)
mengevaluasi ke(list 'one 'two)
yang mengevaluasi ke(list (intern "one") (intern ("two")))
.(intern "one")
membuat simbol bernama "satu" dan menyimpannya di peta hash "pusat", jadi kapan pun Anda mengucapkannya'one
, simbol bernama"one"
akan dicari di peta hash pusat tersebut.Tapi apakah simbol itu?
Misalnya, dalam bahasa OO (Java / Javascript / Python) simbol dapat direpresentasikan sebagai objek yang memiliki
name
bidang, yaitu nama simbol seperti di"one"
atas, dan data dan / atau kode dapat dikaitkan dengannya objek ini.Jadi simbol dalam Python dapat diimplementasikan sebagai:
Dalam Emacs Lisp misalnya, sebuah simbol dapat memiliki 1) data yang terkait dengannya DAN (pada saat yang sama - untuk simbol yang sama) 2) kode yang terkait dengannya - tergantung pada konteksnya, baik data atau kode dipanggil.
Misalnya, di Elisp:
mengevaluasi ke
4
.Karena
(add add add)
dievaluasi sebagai:Jadi, misalnya, menggunakan
Symbol
kelas yang kami definisikan dengan Python di atas,add
Simbol-ELisp ini dapat ditulis dengan Python sebagaiSymbol("add",(lambda x,y: x+y),2)
.Terima kasih banyak untuk teman-teman di IRC #emacs yang telah menjelaskan simbol dan kutipan kepada saya.
sumber
Ketika kita ingin mengirimkan argumen itu sendiri daripada melewatkan nilai argumen, maka kita menggunakan kutipan. Hal ini sebagian besar terkait dengan prosedur yang lewat selama menggunakan daftar, pasangan dan atom yang tidak tersedia dalam Bahasa Pemrograman C (kebanyakan orang memulai pemrograman menggunakan Pemrograman C, Oleh karena itu kami bingung) Ini adalah kode dalam bahasa pemrograman Skema yang merupakan dialek dari cadel dan saya rasa Anda dapat memahami kode ini.
Baris terakhir (atom? 'Abc) melewati abc seperti pada prosedur untuk memeriksa apakah abc adalah atom atau bukan, tetapi ketika Anda melewati (atom? Abc) maka ia memeriksa nilai abc dan meneruskan nilainya ke Itu. Karena, kami belum memberikan nilai apa pun padanya
sumber
Kutipan mengembalikan representasi internal dari argumennya. Setelah membaca terlalu banyak penjelasan tentang apa yang tidak dapat dilakukan kutipan , saat itulah bola lampu menyala. Jika REPL tidak mengubah nama fungsi menjadi UPPER-CASE ketika saya mengutipnya, mungkin saya tidak sadar.
Begitu. Fungsi Lisp biasa mengubah argumennya menjadi representasi internal, mengevaluasi argumen, dan menerapkan fungsinya. Kutipan mengonversi argumennya menjadi representasi internal, dan hanya mengembalikannya. Secara teknis benar untuk mengatakan bahwa kutipan mengatakan, "jangan mengevaluasi", tetapi ketika saya mencoba untuk memahami apa yang dilakukannya, memberi tahu saya apa yang tidak dilakukannya membuat saya frustrasi. Pemanggang roti saya juga tidak mengevaluasi fungsi Lisp; tapi itu bukan cara Anda menjelaskan apa yang dilakukan pemanggang roti.
sumber
Jawaban singkat Anoter:
quote
berarti tanpa mengevaluasinya, dan kutipan balik adalah kutipan tetapi tinggalkan pintu belakang .Referensi yang bagus:
Manual Referensi Emacs Lisp membuatnya sangat jelas
9.3 Mengutip
Kutipan formulir khusus mengembalikan argumen tunggalnya, seperti yang tertulis, tanpa mengevaluasinya. Ini menyediakan cara untuk menyertakan simbol dan daftar konstan, yang bukan merupakan objek evaluasi diri, dalam sebuah program. (Tidak perlu mengutip objek yang mengevaluasi diri sendiri seperti angka, string, dan vektor.)
Bentuk Khusus: objek kutipan
Karena kutipan sering digunakan dalam program, Lisp menyediakan sintaks baca yang nyaman untuknya. Karakter apostrof ('' ') diikuti oleh objek Lisp (dalam sintaks baca) meluas ke daftar yang elemen pertamanya adalah kutipan, dan elemen keduanya adalah objek. Jadi, sintaks baca 'x adalah singkatan dari (kutipan x).
Berikut beberapa contoh ekspresi yang menggunakan kutipan:
9.4 Kutipan Belakang
Konstruksi kutipan balik memungkinkan Anda mengutip daftar, tetapi secara selektif mengevaluasi elemen daftar itu. Dalam kasus yang paling sederhana, ini identik dengan kutipan formulir khusus (dijelaskan di bagian sebelumnya; lihat Mengutip). Misalnya, kedua bentuk ini memberikan hasil yang identik:
Penanda khusus ',' di dalam argumen ke kutipan mundur menunjukkan nilai yang tidak konstan. Evaluator Emacs Lisp mengevaluasi argumen ',', dan meletakkan nilai dalam struktur daftar:
Substitusi dengan ',' juga diperbolehkan pada tingkat yang lebih dalam dari struktur daftar. Sebagai contoh:
Anda juga dapat menggabungkan nilai yang dievaluasi ke dalam daftar yang dihasilkan, menggunakan penanda khusus ', @'. Elemen dari daftar yang disambung menjadi elemen pada tingkat yang sama dengan elemen lain dari daftar yang dihasilkan. Kode yang setara tanpa menggunakan '"' seringkali tidak dapat dibaca. Berikut beberapa contohnya:
sumber
Ini adalah pernyataan klasik yang diketahui oleh programmer cadel.
Saat Anda mengutip kode, kode itu akan menjadi data.
Saat Anda mengutip kode, hasilnya adalah data yang mewakili kode itu. Jadi, ketika Anda ingin bekerja dengan data yang mewakili program, Anda mengutip program itu. Ini juga berlaku untuk ekspresi atom, tidak hanya untuk daftar:
Misalkan Anda ingin membuat bahasa pemrograman yang tertanam dalam cadel - Anda akan bekerja dengan program yang dikutip dalam skema (seperti
'(+ 2 3)
) dan yang ditafsirkan sebagai kode dalam bahasa yang Anda buat, dengan memberikan program interpretasi semantik. Dalam hal ini Anda perlu menggunakan kutipan untuk menyimpan data, jika tidak maka akan dievaluasi dalam bahasa eksternal.sumber