แมโครเพื่อทำสิ่งที่คุณต้องการ
เป็นการออกกำลังกายประเภท:
(defmacro setq-every (value &rest vars)
"Set every variable from VARS to value VALUE."
`(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars)))
ตอนนี้ลอง:
(setq-every "/foo/bar" f-loc1 f-loc2)
มันทำงานยังไง
เนื่องจากผู้คนสงสัยว่ามันทำงานอย่างไร (ตามความคิดเห็น) นี่คือคำอธิบาย หากต้องการเรียนรู้วิธีเขียนแมโครให้เลือกหนังสือ Common Lisp ที่ดี (ใช่ Common LISP คุณจะสามารถทำสิ่งเดียวกันใน Emacs Lisp ได้ แต่ Common LISP นั้นมีพลังมากกว่าและมีหนังสือที่ดีกว่า IMHO)
แมโครทำงานบนรหัสดิบ มาโครไม่ได้ประเมินอาร์กิวเมนต์ของพวกเขา (ต่างจากฟังก์ชั่น) ดังนั้นเราจึงไม่ได้ประเมินค่าvalue
และเก็บสะสมที่นี่vars
สำหรับมาโครของเราเป็นเพียงสัญลักษณ์
progn
จัดกลุ่มหลายsetq
รูปแบบให้เป็นหนึ่งเดียว สิ่งนี้:
(mapcar (lambda (x) (list 'setq x value)) vars)
เพียงสร้างรายการsetq
แบบฟอร์มโดยใช้ตัวอย่างของ OP ซึ่งจะเป็น:
((setq f-loc1 "/foo/bar") (setq f-loc2 "/foo/bar"))
คุณจะเห็นรูปแบบคือภายในของแบบฟอร์ม backquote
,
และจะนำหน้าด้วยเครื่องหมายจุลภาค รูปแบบ backquote ภายในทุกอย่างจะถูกยกมาตามปกติ แต่การประเมินแบบ,
"เปิดเครื่อง" เป็นการชั่วคราวดังนั้นทั้งหมดmapcar
จะถูกประเมินในเวลามหภาค
ในที่สุดก็@
ลบวงเล็บด้านนอกออกจากรายการด้วยsetq
s ดังนั้นเราจะได้รับ:
(progn
(setq f-loc1 "/foo/bar")
(setq f-loc2 "/foo/bar"))
มาโครสามารถแปลงซอร์สโค้ดของคุณเองได้ไม่ดีใช่ไหม
ข้อแม้
นี่คือข้อแม้เล็ก ๆ อาร์กิวเมนต์แรกจะได้รับการประเมินหลายครั้งเพราะมาโครนี้จะขยายต่อไปนี้:
(progn
(setq f-loc1 "/foo/bar")
(setq f-loc2 "/foo/bar"))
คุณจะเห็นว่าถ้าคุณมีตัวแปรหรือสตริงที่นี่มันก็โอเค แต่ถ้าคุณเขียนอะไรเช่นนี้:
(setq-every (my-function-with-side-effects) f-loc1 f-loc2)
จากนั้นฟังก์ชั่นของคุณจะถูกเรียกมากกว่าหนึ่งครั้ง สิ่งนี้อาจไม่พึงประสงค์ นี่คือวิธีแก้ไขด้วยความช่วยเหลือของonce-only
(มีอยู่ใน
แพ็คเกจ 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))))
และปัญหาก็หมดไป