2010年9月8日水曜日

Common Lisp で C 風のswitch

Twitterを眺めていたらネタに出会ったので書いてみました。

(defmacro switch (val &body clauses)
(let ((syms (loop :repeat (length clauses)
:collect (gensym))))
`(tagbody
(case ,val
,@(mapcar
#'(lambda (clause sym)
`(,(car clause) (go ,sym)))
clauses
syms))
,@(mapcan
#'(lambda (clause sym)
`(,sym ,@(cdr clause)))
clauses
syms)
break)))

switchのclauses部にはCommon Lispのcaseと同様の式を書くことができます。

caseと異なるのは、条件に一致した場合goで目的の処理の前に飛ぶところです。このため、明示的に(go break)としない限り、一致した箇所以降の式をすべて実行します。

また、caseと異なり、tagbodyに展開するため返り値は常にnilになります。

;; 例
(switch 2
(2 (print 2) (go break))
(3 (print 3))
(4 (print 4)))
2
=> nil

(switch 3
(2 (print 2) (go break))
(3 (print 3))
(4 (print 4)))
3
4
=> nil

0 件のコメント:

コメントを投稿