球拍开关声明宏

时间:2015-09-26 05:29:28

标签: scheme racket

我试图在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")])

我尝试使用模式匹配语法案例,但我不确定这是正确的方法。这里的任何球拍专家都能给我一个正确的方向吗?

3 个答案:

答案 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")])