Bagaimana cara mengetahui kapan atau kapan tidak menggunakan kutipan tunggal sebelum nama variabel?

31

Saya punya di bawah ini:

(setq some-variable "less")

Saya bingung mengapa saya harus menggunakan kutipan tunggal dengan boundptetapi tidak dengan bound-and-true-p.

Contoh 1:

(when (boundp 'some-variable) 
   (message "some-variable is %s" some-variable))

Hasil:

"beberapa variabel kurang"

Contoh 2a:

(when (bound-and-true-p some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))

Hasil:

"beberapa variabel kurang"

Contoh 2b:

(when (bound-and-true-p 'some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))

Hasil:

dan: Argumen tipe yang salah: symbolp, (kutipan beberapa variabel)

Kaushal Modi
sumber
5
Layak disebutkan yang setqmerupakan singkatan set quoted, dan awalnya adalah makro yang diperluas ke (set 'some-variable "less"). Secara umum, Elisp tidak terlalu konsisten tentang argumen yang dikutip vs yang tidak dikutip, tetapi fungsi apa pun (bukan makro) yang perlu berinteraksi dengan variabel alih-alih nilai akan mengambil argumennya yang dikutip ( setqmenjadi pengecualian utama).
shosti
2
FWIW, bound-and-true-padalah makro yang bodoh. Atau lebih tepatnya, namanya bodoh. 99,99% dari waktu ketika Anda ingin melakukan (and (boundp 'FOO) FOO)Anda melakukannya untuk menggunakan nilaiFOO . Anda tidak melakukannya hanya untuk mendapatkan nilai kebenaran. (1) Makro tidak diperlukan - kode yang diganti sepele dan kecil. (2) Nama itu menyesatkan - ini tentang nilai variabel, bukan hanya menguji apakah nilai variabelnya atau tidak nil.
Drew

Jawaban:

24

Jawaban singkat

Jika Anda mencoba menggunakan variabel itu sendiri, maka gunakan 'some-variable. Jika Anda mencoba menggunakan nilai yang disimpan dalam variabel, gunakan some-variable.

  • boundp menggunakan simbol sehingga akan melihat apa saja yang bisa diikat, termasuk fungsi. Itu hanya peduli apakah ada simbol yang cocok, bukan apa nilainya.
  • bound-and-truep menggunakan var dan mengembalikan nilainya. Dalam hal ini Anda harus memberikan nilai simbol ke fungsi. Jika tidak ada simbol var terikat, atau nilainya nol maka akan mengembalikan nihil.

Penjelasan

Untuk definisi manual, silakan lihat manual .

'dan (quote ...)keduanya melakukan tujuan yang sama di emacs-lisp.

Tujuan dari ini adalah untuk menyerahkan formulir yang tidak dievaluasi ke lingkungan sekitarnya daripada mengevaluasinya.

Dalam contoh Anda anggap kita memiliki yang lebih tinggi berikut

(setq some-variable "less") ;; Rather than just 't for clarity

Kemudian evaluasi berjalan sebagai berikut:

(when (boundp 'some-variable) 
   (message "some-variable is %s" some-variable))
;; ==> (boundp 'some-variable) ; 't
;; ==> some-variable is "less"

Sedangkan tanpa kutipan:

(when (boundp some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))
;; ==> (boundp "less") ; "less" is not a variable. -> Error

Lisp mengevaluasi formulir ketika sudah tercapai, dengan mengutip formulir yang Anda hindari evaluasi sehingga variabel aktual (atau daftar, atau nama fungsi) dilewatkan.

Jonathan Leech-Pepin
sumber
Terima kasih! Jawaban Anda membuat saya melompat ke sumber keduanya dan membantu saya menemukan solusinya. Saya juga memperbarui pertanyaan saya dengan contoh-contoh yang jelas.
Kaushal Modi
6
Saya senang ini membantu op, tetapi sebenarnya tidak menjawab pertanyaan (dengan cara yang bermanfaat bagi orang lain yang memiliki pertanyaan yang sama). Anda hanya menjelaskan bahwa simbol harus dikutip atau dievaluasi. Anda tidak menjelaskan mengapa itu tidak terjadi bound-and-truepdan pertanyaannya adalah mengapa saya harus mengutip ketika menggunakan boundptetapi tidak saat menggunakan bound-and-truep.
tarsius
@tarsius Saya sebenarnya memasukkan perbedaan antara boundpmembutuhkan simbol (tidak dievaluasi) dan bound-and-truepmembutuhkan nilai variabel dalam poin-poin (diedit setelah jawaban awal)
Jonathan Leech-Pepin
Saya pikir sangat penting untuk menyebutkan mengapa demikian (makro dapat memilih untuk tidak mengevaluasi) daripada hanya menyebutkan bahwa doc-string mengatakan demikian. Saya pikir ini adalah pertanyaan yang sangat bagus dan bahwa boundp vs bound-and-true-p hanyalah sebuah contoh. Yang menjadi pertanyaan adalah keinginan untuk belajar tentang aturan evaluasi.
tarsius
@tarsius Bukankah aturan evaluasi dijelaskan oleh jawaban ini? Setidaknya yang mendasar hanya melibatkan kutipan ... Jawaban Anda jelas lebih lengkap, tetapi jauh melampaui topik, bukan?
T. Verron
17

Simbol yang berada di posisi non-fungsi diperlakukan sebagai nama variabel. In (function variable) functionadalah dalam fungsi-posisi (setelah tanda kurung buka) dan variabletidak. Kecuali jika variabel yang dikutip secara eksplisit diganti dengan nilainya.

Jika Anda menulis (boundp my-variable)itu berarti "adalah simbol yang disimpan dalam nilai variabel my-variableterikat sebagai variabel" dan bukan "adalah simbol yang my-variableterikat sebagai variabel.

Jadi mengapa bound-and-truepberperilaku berbeda?

Ini adalah makro dan aturan evaluasi normal (fungsi) tidak berlaku di sini, makro bebas untuk memutuskan apakah dan kapan argumen mereka dievaluasi. Apa yang sebenarnya dilakukan makro adalah entah bagaimana mengubah argumen dan mengembalikan hasilnya sebagai daftar, yang kemudian dievaluasi. Transformasi dan evaluasi akhir terjadi pada waktu yang berbeda, yang disebut waktu ekspansi-makro dan waktu evaluasi.

Seperti inilah definisi bound-and-true-ptampilannya:

(defmacro bound-and-true-p (var)
  "Return the value of symbol VAR if it is bound, else nil."
  `(and (boundp (quote ,var)) ,var))

Ini menggunakan macro pembaca yang berbeda dari macro lisp (lebih lanjut tentang itu di bawah). Untuk tidak mempersulit hal ini lebih lanjut, jangan gunakan makro pembaca apa pun:

(defmacro bound-and-true-p (var)
  "Return the value of symbol VAR if it is bound, else nil."
  (list 'and (list 'boundp (list 'quote var)) var))

Jika kamu menulis

(bound-and-true-p my-variable)

yang pertama "diterjemahkan" ke

(and (boundp 'my-variable) my-variable)

dan kemudian itu dievaluasi kembali niljika my-variabletidak boundpatau nilai my-variable(yang tentu saja bisa juga nil).


Anda mungkin memperhatikan bahwa ekspansi tidak

(and (boundp (quote my-variable)) my-variable)

seperti yang bisa kita harapkan. quoteadalah bentuk khusus, bukan makro atau fungsi. Seperti makro, bentuk khusus dapat melakukan apa pun dengan argumen mereka. Bentuk khusus khusus ini hanya mengembalikan argumennya, di sini simbol, bukan nilai variabel simbol. Itu sebenarnya satu-satunya tujuan bentuk khusus ini: mencegah evaluasi! Macro tidak dapat melakukannya sendiri, mereka perlu menggunakannya quoteuntuk melakukannya.

Jadi ada apa '? Ini adalah makro pembaca , yang seperti disebutkan di atas tidak sama dengan makro pelat . Sementara makro digunakan untuk mengubah kode / data, makro pembaca digunakan sebelumnya ketika membaca teks untuk mengubah teks menjadi kode / data.

'something

adalah bentuk singkat untuk

(quote something)

`definisi sebenarnya yang digunakan bound-and-true-pjuga adalah pembaca makro. Jika mengutip simbol seperti di `symboldalamnya setara dengan 'symbol, tetapi ketika digunakan untuk mengutip daftar seperti di `(foo bar ,baz)dalamnya berperilaku berbeda dalam bentuk yang diawali dengan ,dievaluasi.

`(constant ,variable)

setara dengan

(list (quote constant) variable))

Ini harus menjawab pertanyaan mengapa simbol-simbol yang tidak dikutip kadang-kadang dievaluasi (diganti dengan nilainya) dan terkadang tidak; makro dapat digunakan quoteuntuk mencegah simbol dievaluasi.

Tapi mengapa bound-and-true-pmakro sementara boundptidak? Kita harus dapat menentukan apakah simbol sewenang-wenang, yang tidak dikenal sampai waktu berjalan, terikat sebagai simbol. Ini tidak akan mungkin terjadi jika boundpargumen dikutip secara otomatis.

bound-and-true-pdigunakan untuk menentukan apakah variabel yang dikenal didefinisikan dan jika demikian gunakan nilainya. Ini berguna jika perpustakaan memiliki ketergantungan opsional pada perpustakaan pihak ketiga seperti pada:

(defun foo-get-value ()
  (or (bound-and-true-p bar-value)
      ;; we have to calculate the value ourselves
      (our own inefficient or otherwise undesirable variant)))

bound-and-true-pdapat didefinisikan sebagai fungsi dan memerlukan argumen untuk dikutip tetapi karena itu dimaksudkan untuk kasus-kasus di mana Anda tahu di muka variabel apa yang Anda pedulikan tentang makro yang digunakan untuk menyelamatkan Anda dari keharusan mengetik '.

tarsius
sumber
Jawaban yang sangat bagus.
Charles Ritchie
2

Dari kode sumber boundp:

DEFUN ("boundp", Fboundp, Sboundp, 1, 1, 0,
      doc: /* Return t if SYMBOL's value is not void.
Note that if `lexical-binding' is in effect, this refers to the
global value outside of any lexical scope.  */)

boundpmengharapkan a symbolsebagai input. 'some-variableadalah simbol untuk variabel some-variable.

Dari kode sumber bound-and-true-p:

(defmacro bound-and-true-p (var)
  "Return the value of symbol VAR if it is bound, else nil."
  `(and (boundp (quote ,var)) ,var))

bound-and-true-pmengharapkan a variablesebagai input.

Di dalam bound-and-true-pmakro, ia mendapat simbol dengan melakukan (quote ,var). Jadi jika inputnya adalah some-variable, (quote ,var)akan menghasilkan 'some-variable.

Tapi ketika saya memberikan masukan 'some-variableuntuk bound-and-true-p, saya mendapatkan error: and: Wrong type argument: symbolp, (quote some-variable)karena makro TIDAK mengharapkan simbol ( 'some-variable) pada masukan.

Kaushal Modi
sumber
Hanya kepala-up. ''symbol memang masuk akal. Artinya (quote (quote symbol)). Alasan Anda mendapatkan kesalahan adalah karena itu bukan argumen yang valid boundp.
Malabarba
@Malabarba Terima kasih. Saya sudah melakukan koreksi.
Kaushal Modi