Common Lisp case
宏總是默認爲eql
,用於測試其keyform是否與其子句中的某個鍵相匹配。我正在與下面的宏旨在概括case
使用任何提供的比較函數(儘管與評估鍵):選擇/評估宏參數形式
(defmacro case-test (form test &rest clauses)
(once-only (form test)
`(cond ,@(mapcar #'(lambda (clause)
`((funcall ,test ,form ,(car clause))
,@(cdr clause)))
`,clauses))))
使用
(defmacro once-only ((&rest names) &body body)
"Ensures macro arguments only evaluate once and in order.
Wrap around a backquoted macro expansion."
(let ((gensyms (loop for nil in names collect (gensym))))
`(let (,@(loop for g in gensyms collect `(,g (gensym))))
`(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))
,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))
,@body)))))
例如:
(macroexpand '(case-test (list 3 4) #'equal
('(1 2) 'a 'b)
('(3 4) 'c 'd)))
給出
(LET ((#:G527 (LIST 3 4)) (#:G528 #'EQUAL))
(COND ((FUNCALL #:G528 #:G527 '(1 2)) 'A 'B)
((FUNCALL #:G528 #:G527 '(3 4)) 'C 'D)))
是否有必要擔心函數參數的宏變量捕獲(如#'equal)?如果
once-only
列表中沒有這樣的參數,或者如果#'equal
也是keyform的一部分,那麼是否仍然存在潛在的衝突。 Paul Graham在他的着作On Lisp,第118頁中說,一些可變捕獲衝突導致「極其微妙的錯誤」,導致人們相信它可能更適合所有的事情。傳遞測試名稱(如
equal
)而不是函數對象(如#'equal)是否更靈活?看起來你可以直接將名稱放在函數調用位置(而不是使用funcall
),並允許使用宏和特殊形式以及函數?可能是
case-test
而不是是函數,而不是宏?
參見['fcase '](http://clisp.org/impnotes/fcase.html)在CLISP中。 – sds
亞歷山大也有'SWITCH'爲此。 – jkiiski
這與Lisp Machine Lisp中的SELECTOR類似。約1980. https://common-lisp.net/svn/mit-cadr/trunk/lisp/sys2/lmmac.lisp雖然SELECTOR不評估測試功能。 –