我试图在Racket中创建一个switch语句宏。我很难搞清楚如何做到这一点。我希望能够使用如下所示的功能。
(define x 99)
(switch x
[3 (displayln "x is 3")]
[4 (displayln "x is 4")]
[5 (displayln "x is 5")]
['default (displayln "none of the above")])
我尝试使用模式匹配语法案例,但我不确定这是正确的方法。这里的任何球拍专家都能给我一个正确的方向吗?
答案 0 :(得分:2)
我相信不会重塑完美的轮子,所以这里有一个宏,可以将你的switch
转换为等效的case
,稍微更改一下,使用default
代替{ {1}}。 (我向soegaard道歉,因为我不知道如何使用'default
,所以我只会坚持使用传统的syntax-parse
。 - ))
syntax-case
与使用(define-syntax (switch stx)
(define (transform-clause cl)
(syntax-case cl (default)
((default expr) #'(else expr))
((val ... expr) #'((val ...) expr))))
(define (transform-clauses cls)
(syntax-case cls ()
((cl)
(with-syntax ((case-clause (transform-clause #'cl)))
#'(case-clause)))
((cl rest ...)
(with-syntax ((case-clause (transform-clause #'cl))
((case-rest ...) (transform-clauses #'(rest ...))))
#'(case-clause case-rest ...)))))
(syntax-case stx ()
((_ x clause ...)
(with-syntax (((case-clause ...) (transform-clauses #'(clause ...))))
#'(case x case-clause ...)))))
相比,使用此switch
宏的缺点是您无法区分使用case
来匹配符号default
, vs作为全能。因此使用default
仍然更好。 : - )
case
答案 1 :(得分:2)
我喜欢其他两个答案,但我觉得我们还应该提一下现有的match
表单,其中(afaict)已经完全符合您的要求:
#lang racket
(define x 99)
(match x
[3 (displayln "x is 3")]
[4 (displayln "x is 4")]
[5 (displayln "x is 5")]
[default (displayln "none of the above")])
唯一的变化:我写match
而不是switch
,我使用模式default
而不是引用的'default
。事实上,任何标识符都适用于此;标识符只是给出一个值的名称。
事实上,匹配可以比这更多,但这是一个很好的用途。
如果您只是在寻找编写宏的练习,那么您可以忽略这个答案:)。
答案 2 :(得分:1)
对于switch
我建议制作一个可以打开一个值的辅助宏 - 这样可以避免在expr
中多次评估(switch expr clause ...)
的问题。
下面我使用标识符else
来表示默认子句。
请注意syntax-parse
一次尝试一次模式。这用于在给定错误输入(例如(switch)
模式中的初始标识符以_
为前缀,以避免递归宏中出现任何问题。
请注意,使用的基本重写规则是:
(switch-value v
[expr result]
clause ...)
==>
(if (equal? v expr)
result
(switch-value v clause ...))
使宏递归比编写一个一次处理所有子句的大宏更容易。
#lang racket
(require (for-syntax syntax/parse))
(define-syntax (switch stx)
(syntax-parse stx
[(_switch)
#'(raise-syntax-error 'switch "value expression and at least one clause expected" stx)]
[(_switch expr clause ...)
#'(let ([v expr])
(switch-value v clause ...))]))
(define-syntax (switch-value stx)
(syntax-parse stx
#:literals (else)
[(_switch-value v)
#'(raise-syntax-error 'switch "at least one clause is expected" _switch-value)]
[(_switch-value v [else expr])
#'expr]
[(_switch-value v [expr1 expr2] clause ...)
#'(if (equal? v expr1)
expr2
(switch-value v clause ...))]))
(define x 4)
(switch x
[3 "x is 3"]
[4 "x is 4"]
[5 "x is 5"]
[else (displayln "none of the above")])