具有語法規則的衛生和引用透明的巨集
LISP 和 Scheme 相比其他主流程式語言的最大優勢是它們的巨集系統。與 C 前處理器和其他巨集語言不同,Scheme 巨集將解析後的程式碼作為輸入,並將擴充套件程式碼作為輸出返回。這是 Scheme 的程式碼是資料短語的應用之一,它使語言如此強大。
Scheme 中的巨集是用 define-syntax
建立的,define-syntax
可以通過多種方式定義巨集。最簡單的方法是使用 syntax-rules
,它使用模式匹配將輸入程式碼轉換為輸出程式碼。
此示例建立一個簡單的 for item in list
和 for list as item
語法,用於迴圈列表中的元素:
(define-syntax for
(syntax-rules (in as) ; 'in' and 'as' keywords must match in the pattern
; When the 'for' macro is called, try matching this pattern
((for element in list
body ...) ; Match one or more body expressions
; Transform the input code
(for-each (lambda (element)
body ...)
list))
; Try matching another pattern if the first fails
((for list as element
body ...)
; Use the existing macro for the transform
(for element in list
body ...))))
然後可以按如下方式使用這兩個巨集,從而提供更強制的風格:
(let ((names '(Alice Bob Eve)))
(for name in names
(display "Hello ")
(display name)
(newline))
(for names as name
(display "name: ")
(display name)
(newline)))
執行程式碼將提供預期的輸出:
Hello Alice
Hello Bob
Hello Eve
name: Alice
name: Bob
name: Eve
需要注意的最常見錯誤是沒有將正確的值傳遞給巨集,這通常會導致無效的錯誤訊息應用於擴充套件形式而不是巨集呼叫。
上面的 for
語法定義不檢查它們是否傳遞了識別符號和列表,因此傳遞任何其他型別將導致指向 for-each
呼叫而不是 for
呼叫的錯誤。除錯這會破壞巨集的目的,因此由使用者將檢查放在那裡並報告使用錯誤,然後可以在編譯時捕獲。