Makro untuk melakukan apa yang Anda inginkan
Sebagai latihan sejenis:
(defmacro setq-every (value &rest vars)
"Set every variable from VARS to value VALUE."
`(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars)))
Sekarang coba:
(setq-every "/foo/bar" f-loc1 f-loc2)
bagaimana cara kerjanya
Karena orang ingin tahu bagaimana cara kerjanya (menurut komentar), berikut adalah penjelasannya. Untuk benar-benar mempelajari cara menulis makro, pilih buku Common Lisp yang baik (ya, Common Lisp, Anda akan dapat melakukan hal yang sama di Emacs Lisp, tetapi Common Lisp sedikit lebih kuat dan memiliki buku yang lebih baik, IMHO).
Macro beroperasi pada kode mentah. Makro tidak mengevaluasi argumen mereka (tidak seperti fungsi). Jadi kita di sini tidak dievaluasi value
dan koleksi vars
, yang untuk makro kita hanya simbol.
progn
kelompok beberapa setq
bentuk menjadi satu. Hal ini:
(mapcar (lambda (x) (list 'setq x value)) vars)
Hanya menghasilkan daftar setq
formulir, menggunakan contoh OP itu akan menjadi:
((setq f-loc1 "/foo/bar") (setq f-loc2 "/foo/bar"))
Anda lihat, formulir itu di dalam formulir backquote dan diawali dengan koma
,
. Di dalam formulir backquoted semuanya dikutip seperti biasa, tetapi ,
evaluasi " mapcar
nyalakan " sementara, sehingga keseluruhan dievaluasi pada waktu ekspansi makro.
Akhirnya @
menghapus tanda kurung luar dari daftar dengan setq
s, jadi kami mendapatkan:
(progn
(setq f-loc1 "/foo/bar")
(setq f-loc2 "/foo/bar"))
Macro dapat mengubah kode sumber Anda secara sewenang-wenang, bukankah hebat?
Peringatan
Berikut ini adalah peringatan kecil, argumen pertama akan dievaluasi beberapa kali, karena makro ini pada dasarnya diperluas sebagai berikut:
(progn
(setq f-loc1 "/foo/bar")
(setq f-loc2 "/foo/bar"))
Anda lihat, jika Anda memiliki variabel atau string di sini tidak apa-apa, tetapi jika Anda menulis sesuatu seperti ini:
(setq-every (my-function-with-side-effects) f-loc1 f-loc2)
Maka fungsi Anda akan dipanggil lebih dari sekali. Ini mungkin tidak diinginkan. Berikut ini cara memperbaikinya dengan bantuan once-only
(tersedia dalam
paket MMT ):
(defmacro setq-every (value &rest vars)
"Set every variable from VARS to value VALUE.
VALUE is only evaluated once."
(mmt-once-only (value)
`(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars))))
Dan masalahnya hilang.