เพื่อให้คำตอบสั้น ๆ แมโครจะใช้สำหรับการกำหนดส่วนขยายไวยากรณ์ภาษาเพื่อ Common Lisp หรือ Domain Specific Languages (DSLs) ภาษาเหล่านี้ฝังอยู่ในรหัสเสียงกระเพื่อมที่มีอยู่ ตอนนี้ DSL สามารถมีไวยากรณ์คล้ายกับ Lisp (เช่นProlog Interpreterสำหรับ Lisp ของ Peter Norvig ) หรือแตกต่างอย่างสิ้นเชิง (เช่นInfix Notation Mathสำหรับ Clojure)
นี่เป็นตัวอย่างที่เป็นรูปธรรมมากขึ้น:
Python มีรายการความเข้าใจในภาษา สิ่งนี้จะให้ไวยากรณ์ที่ง่ายสำหรับกรณีทั่วไป เส้น
divisibleByTwo = [x for x in range(10) if x % 2 == 0]
ให้ผลลัพธ์รายการที่มีตัวเลขคู่ทั้งหมดตั้งแต่ 0 ถึง 9 ย้อนกลับไปในPython 1.5วันที่ไม่มีไวยากรณ์ดังกล่าว คุณจะใช้สิ่งนี้มากกว่านี้:
divisibleByTwo = []
for x in range( 10 ):
if x % 2 == 0:
divisibleByTwo.append( x )
สิ่งเหล่านี้มีหน้าที่เทียบเท่ากัน เราขอหยุดการไม่เชื่อและแกล้งทำเป็น Lisp มีมาโครวง จำกัด ที่ทำซ้ำและไม่มีวิธีง่ายๆในการทำรายการเทียบเท่าความเข้าใจ
ใน Lisp คุณสามารถเขียนสิ่งต่อไปนี้ ฉันควรทราบว่าตัวอย่างที่ถูกประดิษฐ์นี้ถูกเลือกให้เหมือนกับรหัส Python ไม่ใช่ตัวอย่างที่ดีของ Lisp code
;; the following two functions just make equivalent of Python's range function
;; you can safely ignore them unless you are running this code
(defun range-helper (x)
(if (= x 0)
(list x)
(cons x (range-helper (- x 1)))))
(defun range (x)
(reverse (range-helper (- x 1))))
;; equivalent to the python example:
;; define a variable
(defvar divisibleByTwo nil)
;; loop from 0 upto and including 9
(loop for x in (range 10)
;; test for divisibility by two
if (= (mod x 2) 0)
;; append to the list
do (setq divisibleByTwo (append divisibleByTwo (list x))))
ก่อนที่ฉันจะไปต่อฉันควรอธิบายว่าแมโครคืออะไร มันคือการแปลงที่ดำเนินการกับรหัสด้วยรหัส นั่นคือส่วนหนึ่งของรหัสอ่านโดยล่าม (หรือคอมไพเลอร์) ซึ่งใช้เป็นรหัสในการโต้แย้งจัดการและผลตอบแทนที่ได้ซึ่งจะทำงานในสถานที่
แน่นอนว่าการพิมพ์จำนวนมากและโปรแกรมเมอร์นั้นขี้เกียจ ดังนั้นเราสามารถกำหนด DSL สำหรับทำรายการความเข้าใจ ในความเป็นจริงเราใช้มาโครหนึ่งตัวอยู่แล้ว (มาโครวนรอบ)
เสียงกระเพื่อมกำหนดสองรูปแบบไวยากรณ์พิเศษ เครื่องหมายคำพูด ( '
) ระบุโทเค็นถัดไปเป็นตัวอักษร quasiquote หรือ backtick ( `
) ระบุโทเค็นถัดไปเป็นตัวอักษรที่มีทางหนี Escapes ถูกระบุโดยโอเปอเรเตอร์จุลภาค ตัวหนังสือเทียบเท่าของงูหลามของ'(1 2 3)
[1, 2, 3]
คุณสามารถกำหนดให้กับตัวแปรอื่นหรือใช้แทน คุณสามารถคิดได้ว่า`(1 2 ,x)
เทียบเท่ากับ Python [1, 2, x]
ซึ่งx
เป็นตัวแปรที่กำหนดไว้ก่อนหน้านี้ สัญลักษณ์รายการนี้เป็นส่วนหนึ่งของเวทย์มนตร์ที่เข้าสู่มาโคร ส่วนที่สองคือตัวอ่านเสียงกระเพื่อมซึ่งแทนที่มาโครอย่างชาญฉลาดสำหรับโค้ด แต่เป็นภาพที่ดีที่สุดด้านล่าง:
ดังนั้นเราสามารถกำหนดแมโครที่เรียกว่าlcomp
(ย่อมาจาก list comprehension) ไวยากรณ์ของมันจะเหมือนกับงูหลามที่เราใช้ในตัวอย่าง[x for x in range(10) if x % 2 == 0]
-(lcomp x for x in (range 10) if (= (% x 2) 0))
(defmacro lcomp (expression for var in list conditional conditional-test)
;; create a unique variable name for the result
(let ((result (gensym)))
;; the arguments are really code so we can substitute them
;; store nil in the unique variable name generated above
`(let ((,result nil))
;; var is a variable name
;; list is the list literal we are suppose to iterate over
(loop for ,var in ,list
;; conditional is if or unless
;; conditional-test is (= (mod x 2) 0) in our examples
,conditional ,conditional-test
;; and this is the action from the earlier lisp example
;; result = result + [x] in python
do (setq ,result (append ,result (list ,expression))))
;; return the result
,result)))
ตอนนี้เราสามารถดำเนินการได้ที่บรรทัดคำสั่ง:
CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)
ค่อนข้างเรียบร้อยใช่มั้ย ตอนนี้มันไม่หยุดแค่นั้น คุณมีกลไกหรือพู่กันถ้าคุณชอบ คุณสามารถมีไวยากรณ์ใด ๆ ที่คุณอาจต้องการ เช่นเดียวกับ Python หรือwith
ไวยากรณ์ของ C # หรือไวยากรณ์ LINQ ของ. NET ท้ายที่สุดนี่คือสิ่งที่ดึงดูดผู้คนสู่ Lisp - ความยืดหยุ่นสูงสุด