Kapan harus mengutip dengan tajam ekspresi lambda?

30

T: Kapan, jika pernah, berguna untuk mengutip dengan tajam lambda, dan kapan, haruskah kita tidak mengutip dengan tajam lambda?

Orang menggunakan lambdas dalam tiga cara:

  1. polos: (lambda (x) x)
  2. dikutip: '(lambda (x) x)
  3. dikutip tajam: #'(lambda (x) x)

Utas SO ini membahas tiga jenis, utas SO ini menjelaskan mengapa tidak mengutip (NB: bukan kutipan tajam ) lambda, dan utas SO ini juga membahas perbedaan antara kutipan dan kutipan tajam.

Sekarang, simpul manual pada fungsi anonim dan dokumentasi untuk lambdacatatan bahwa lambdas adalah kutipan sendiri:

Panggilan formulir (lambda ARGS DOCSTRING INTERACTIVE BODY)mengutip diri sendiri; hasil mengevaluasi ekspresi lambda adalah ekspresi itu sendiri. Ekspresi lambda kemudian dapat diperlakukan sebagai fungsi ...

Jadi, tampaknya itu (lambda (x) x)dan #'(lambda (x) x)setara, tetapi '(lambda (x) x)tidak (yang paling penting, ketika byte-compiling).

Sepertinya satu jarang ingin mengutip sebuah lambda, tetapi tidak jelas bagi saya ketika, jika pernah, kita harus, atau tidak harus, tajam-quote:

  • Apakah mengutip dengan tajam lambdahanyalah pilihan gaya, atau adakah situasi di mana kutipan tajam itu bermanfaat?
  • Apakah ada keadaan di mana kita tidak boleh mengutip dengan tajam a lambda, yaitu, ketika melakukan itu akan mengubah makna kode?
Dan
sumber

Jawaban:

28

Sekali waktu , kutipan tajam diperlukan untuk lambdas, sekarang itu tidak lagi terjadi.

Jadi, tampaknya (lambda (x) x) dan # '(lambda (x) x) adalah setara, tetapi' (lambda (x) x) tidak (yang paling penting, ketika byte-compiling).

Iya nih. Bahkan, dua yang pertama benar-benar identik ketika dievaluasi. Seperti dijelaskan di halaman manual yang Anda tautkan:

Formulir berikut semuanya setara:

(lambda (x) (* x x)) 
(function (lambda (x) (* x x))) 
#'(lambda (x) (* x x))

Selain mencoba mendukung versi Emacs dari dua dekade lalu, tidak pernah ada alasan untuk mengutip tajam lambda.

Jadi jangan.


Sebagai sidenote:

  • Sulit mengutip lambda (with ') tidak membuat perbedaan, itu mencegah kompilasi byte. Saya tidak bisa memikirkan skenario di mana itu berguna, tetapi siapa yang tahu.

  • Backtic adalah satu-satunya kutipan yang benar-benar berguna untuk lambdas, tetapi hanya jika Anda tidak menggunakan pengikatan leksikal untuk beberapa alasan.

Malabarba
sumber
Pertimbangkan untuk menambahkan tautan ke bagian Fungsi Anonim dari manual yang berisi contoh yang menjelaskan efek mengutip pada kompilasi byte.
Constantine
@Constantine Done. Saya malas karena saya menggunakan telepon, dan OP sudah menghubungkannya.
Malabarba
Bisakah Anda mengklarifikasi maksud Anda tentang tidak menggunakan pengikatan leksikal dan backtick? Terima kasih.
coredump
@coredump Dengan pengikatan dinamis, satu-satunya cara untuk membuat variabel luar dapat diakses di dalam lambda adalah dengan secara manual membangun lambda sebagai daftar dengan variabel di dalamnya. Backtics bagus untuk hal semacam itu.
Malabarba
BTW, saya tidak berpikir "pada suatu waktu" benar-benar berlaku: ketika saya menyelidiki subjek ini dalam sejarah revisi, saya menemukan bahwa lambdatelah didefinisikan sebagai makro yang menambahkan functionsebanyak mungkin kembali. TKI jika #'diperlukan di beberapa titik, itu dalam kode pengembangan yang sangat awal. Itu pasti tidak diperlukan sudah di Emacs-18.
Stefan
5

Karena lambdatidak masuk akal ketika tidak dikutip, versi terbaru dari Emacs Lisp mengikuti (ANSI) Common Lisp dalam menafsirkan tanda kutip (lambda...)sebagai #'(lambda...). Kedua notasi tersebut hampir persis sama (kecuali dalam struktur yang dikutip).

Apakah lebih suka (lambda...)atau #'(lambda...)karena itu murni masalah gaya. Beberapa orang lebih suka bentuk telanjang, yang menghindari kebisingan sintaksis, sementara yang lain (termasuk saya) lebih suka bentuk yang dikutip.

jch
sumber
Ini bertentangan dengan manual elisp: "Dalam Emacs Lisp, daftar tersebut adalah ekspresi yang valid yang mengevaluasi ke objek fungsi."
djechlin
8
"Versi terbaru" seperti dalam "versi yang dirilis setelah 1990 atau lebih" ;-)
Stefan
0

Menambahkan sedikit sejarah tambahan, karena melihat warisan sejarah Is # '(lambda ...)?

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=4290 menyarankan bahwa:

Dimulai dengan Emacs 22, lambdaformulir byte-dikompilasi ketika digunakan sebagai fungsi, terlepas dari apakah itu didahului oleh functionatau #'. Dengan versi Emacs sebelum 22, Anda harus secara eksplisit menggunakan #' atau functionjika Anda ingin formulir itu dikompilasi dengan byte.

Saya tidak tahu tentang byte-compiler, tapi saya bisa melihat bahwa setidaknya pada tahun 1993 lambdamakro itu sendiri mengembalikan (function (lambda ...))formulir.

https://www.iro.umontreal.ca/~monnier/hopl-4-emacs-lisp.pdf juga mengatakan:

Menariknya (tidak seperti MacLisp), lambdasecara teknis tidak menjadi bagian dari bahasa Elisp sampai sekitar tahun 1991 ketika ditambahkan sebagai makro, awal selama pengembangan Emacs-19. Dalam Emacs-18, fungsi anonim ditulis sebagai nilai yang dikutip dari formulir:

'(lambda (..ARGS..) ..BODY..)

Sementara lambdamakro telah membuat kutipan ini tidak perlu selama hampir 30 tahun sekarang, banyak contoh praktik ini masih terjadi dalam kode Elisp, meskipun mencegah byte-kompilasi dari tubuh. Agak terkait, hanya pada tahun 1993 Lucid Emacs 19.8 mengimpor #'...singkatan untuk pembaca (function ...)dari MacLisp. Emacs mengikuti tahun berikutnya.

phils
sumber
0

Hanya ingin memberikan contoh praktis menggunakan ekspresi lambda backtic. Ini tentang lexical-binding / variable shadowing, menggunakan ekspresi lambda backtic dan referensi variabel dengan koma memungkinkan untuk mendapatkan nilai global mereka.

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall `(lambda()
             (let ((my-variable "Random"))
               (message ,my-variable)))))

M-x [RET] eval-buffer keluaran "Acak lama"

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall (lambda()
             (let ((my-variable "Random"))
               (message my-variable)))))

M-x [RET] eval-buffer output "Acak"

Contoh ketiga menggabungkan variabel global dan variabel lokal

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall `(lambda()
              (let ((my-variable "Random"))
                (message my-variable)
                (message ,my-variable)))))

M-x [RET] eval-buffer output "Acak" "Acak lama"

cjohansson
sumber
@npostavs bukan itu intinya dengan contoh saya tapi saya memodifikasi contoh saya untuk menghindari praktik buruk itu juga
cjohansson
Lebih baik, meskipun saya masih tidak yakin ini adalah peningkatan dari hanya memilih nama yang berbeda untuk ikatan batin.
npostavs