Một macro để làm những gì bạn muốn
Như một bài tập của một loại:
(defmacro setq-every (value &rest vars)
"Set every variable from VARS to value VALUE."
`(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars)))
Bây giờ hãy thử nó:
(setq-every "/foo/bar" f-loc1 f-loc2)
Làm thế nào nó hoạt động
Vì mọi người tò mò về cách thức hoạt động của nó (theo ý kiến), đây là một lời giải thích. Để thực sự học cách viết macro, hãy chọn một cuốn sách Common Lisp tốt (vâng, Common Lisp, bạn sẽ có thể làm những thứ tương tự trong Emacs Lisp, nhưng Common Lisp mạnh hơn một chút và có sách hay hơn, IMHO).
Macro hoạt động trên mã thô. Các macro không đánh giá các đối số của chúng (không giống như các hàm). Vì vậy, chúng tôi đã đánh giá value
và thu thập ở đây vars
, mà đối với vĩ mô của chúng tôi chỉ là biểu tượng.
progn
nhóm một số setq
hình thức thành một. Thứ này:
(mapcar (lambda (x) (list 'setq x value)) vars)
Chỉ cần tạo một danh sách các setq
biểu mẫu, sử dụng ví dụ của OP sẽ là:
((setq f-loc1 "/foo/bar") (setq f-loc2 "/foo/bar"))
Bạn thấy đấy, biểu mẫu nằm trong biểu mẫu backquote và được thêm tiền tố bằng dấu phẩy
,
. Bên trong biểu mẫu được trích dẫn lại, mọi thứ đều được trích dẫn như bình thường, nhưng ,
tạm thời đánh giá lượt bật, vì vậy toàn bộ mapcar
được đánh giá tại thời điểm mở rộng vĩ mô.
Cuối cùng @
loại bỏ dấu ngoặc đơn bên ngoài khỏi danh sách với setq
s, vì vậy chúng tôi nhận được:
(progn
(setq f-loc1 "/foo/bar")
(setq f-loc2 "/foo/bar"))
Macro có thể tùy ý chuyển đổi mã nguồn của bạn, thật tuyệt phải không?
Một cảnh báo
Đây là một cảnh báo nhỏ, đối số đầu tiên sẽ được đánh giá nhiều lần, bởi vì macro này về cơ bản mở rộng ra như sau:
(progn
(setq f-loc1 "/foo/bar")
(setq f-loc2 "/foo/bar"))
Bạn thấy đấy, nếu bạn có một biến hoặc chuỗi ở đây thì không sao, nhưng nếu bạn viết một cái gì đó như thế này:
(setq-every (my-function-with-side-effects) f-loc1 f-loc2)
Sau đó, chức năng của bạn sẽ được gọi nhiều hơn một lần. Điều này có thể là không mong muốn. Dưới đây là cách khắc phục với sự trợ giúp của once-only
(có sẵn trong
gói 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))))
Và vấn đề đã biến mất.