Kapan menggunakan '(atau kutipan) di Lisp?


114

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:


178

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 adan 2. Nilai simbol adicari di set pengikatan variabel saat ini, dan kemudian diganti. Say asaat 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 quotemasuk. 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 allocatedan freedijunjung 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, listbisa lebih mudah dibaca.

Hei, Anda Lupa quote!

Jadi, bagaimana ini meninggalkan kita? Oh benar, quotesebenarnya 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-symboldalam ruang lingkup saat ini!

Menyimpulkan

quote, backquote(dengan koma), dan listadalah beberapa alat yang Anda gunakan untuk membuat daftar, yang tidak hanya daftar nilai, tetapi seperti yang Anda lihat dapat digunakan sebagai structstruktur 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!


Di emacs saya SBCL sudah diatur dan ketika saya mengetik "'this' is 'true` Ini hanya mengembalikan yang terakhir yaitu BENAR dalam keluaran. Bahkan dalam portacle saya mendapatkan output yang sama
Totoro

@Totoro Nilai yang dikembalikan dari suatu fungsi atau hanya beberapa pernyataan dalam cadel adalah ekspresi terakhir, jadi ia benar-benar mengembalikan this, lalu is, lalu true, tetapi Anda hanya melihat yang terakhir dikembalikan. (ini dan benar adalah pernyataan terpisah)
Wezl

52

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"


Bagaimana bisa mengevaluasinya lalu misalnya ada unquoteperintah?
Kapur

3
@William Lisps memiliki fungsi yang nyaman disebut 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.
darkfeline

18

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.


Jadi sepertinya Anda harus menggunakan (list (+ 1 2)) sebagian besar waktu. Jika demikian, bagaimana Anda mencegah evaluasi (+ 1 2)di dalam contoh seperti itu? Apakah ada unquoteperintah?
Kapur

1
Apakah Anda ingin yang setara '((3)), atau yang setara dengan '((+ 1 2))? Jika yang terakhir, Anda harus menggunakan lebih list: (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.
Xanthir

1
Apakah Anda keberatan merujuk di mana daftar yang dikutip seharusnya diperlakukan sebagai konstanta?
Kapur

HyperSpec clhs.lisp.se/Body/s_quote.htm mengatakan bahwa perilaku tidak ditentukan jika objek yang dikutip dimodifikasi secara destruktif. Ini tersirat bahwa ini untuk memungkinkan impls memperlakukan nilai sebagai nilai atom.
Xanthir

14

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!


11

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:

(quote a)
'a

Ini juga dapat digunakan untuk membuat daftar (atau pohon):

(quote (1 2 3))
'(1 2 3)

Anda mungkin lebih baik mendapatkan buku pengantar tentang cadel, seperti Practical Common Lisp (yang tersedia untuk dibaca on-line).


3

Di Emacs Lisp:

Apa yang bisa dikutip?

Daftar dan simbol.

Mengutip bilangan berarti mengevaluasi bilangan itu sendiri: '5sama dengan 5.

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 namebidang, yaitu nama simbol seperti di "one"atas, dan data dan / atau kode dapat dikaitkan dengannya objek ini.

Jadi simbol dalam Python dapat diimplementasikan sebagai:

class Symbol:
   def __init__(self,name,code,value):
       self.name=name
       self.code=code
       self.value=value

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:

(progn
  (fset 'add '+ )
  (set 'add 2)
  (add add add)
)

mengevaluasi ke 4.

Karena (add add add)dievaluasi sebagai:

(add add add)
(+ add add)
(+ 2 add)
(+ 2 2)
4

Jadi, misalnya, menggunakan Symbolkelas yang kami definisikan dengan Python di atas, addSimbol-ELisp ini dapat ditulis dengan Python sebagai Symbol("add",(lambda x,y: x+y),2).

Terima kasih banyak untuk teman-teman di IRC #emacs yang telah menjelaskan simbol dan kutipan kepada saya.


2

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.

(define atom?              ; defining a procedure atom?
  (lambda (x)              ; which as one argument x
(and (not (null? x)) (not(pair? x) )))) ; checks if the argument is atom or not
(atom? '(a b c)) ; since it is a list it is false #f

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


1

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.


1

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

This special form returns object, without evaluating it. 

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:

(quote (+ 1 2))
      (+ 1 2)

(quote foo)
      foo

'foo
      foo

''foo
      (quote foo)

'(quote foo)
      (quote foo)

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:

`(a list of (+ 2 3) elements)
      (a list of (+ 2 3) elements)

'(a list of (+ 2 3) elements)
      (a list of (+ 2 3) elements)

Penanda khusus ',' di dalam argumen ke kutipan mundur menunjukkan nilai yang tidak konstan. Evaluator Emacs Lisp mengevaluasi argumen ',', dan meletakkan nilai dalam struktur daftar:

`(a list of ,(+ 2 3) elements)
      (a list of 5 elements)

Substitusi dengan ',' juga diperbolehkan pada tingkat yang lebih dalam dari struktur daftar. Sebagai contoh:

`(1 2 (3 ,(+ 4 5)))
      (1 2 (3 9))

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:

(setq some-list '(2 3))
      (2 3)

(cons 1 (append some-list '(4) some-list))
      (1 2 3 4 2 3)

`(1 ,@some-list 4 ,@some-list)
      (1 2 3 4 2 3)

1
Code is data and data is code.  There is no clear distinction between them.

Ini adalah pernyataan klasik yang diketahui oleh programmer cadel.

Saat Anda mengutip kode, kode itu akan menjadi data.

1 ]=> '(+ 2 3 4)
;Value: (+ 2 3 4)

1 ]=> (+ 2 3 4)
;Value: 9

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:

1 ]=> 'code
;Value: code

1 ]=> '10
;Value: 10

1 ]=> '"ok"
;Value: "ok"

1 ]=> code
;Unbound variable: code

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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.