首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使宏与“`match`”一起工作?

如何使宏与“`match`”一起工作?
EN

Stack Overflow用户
提问于 2020-03-30 01:30:15
回答 2查看 88关注 0票数 1

我试图为我用racket/gui制作的计算器编写一些类似于有限状态机的东西,并且我决定使用casematch的混合来实现它。对于特定的状态和符号,我将执行一些任意代码,并返回机器的下一个状态。一个简单的例子:

代码语言:javascript
复制
  (case current-state
    [(state-1)
     (match symbol
       [(? predicate-1?) 
        (some-action)
        next-state]
       [(? predicate-2?) 
        (some-action)
        next-state]
       ; ...
       )]
    ; ...
    )

不过,我想让它更容易阅读,并想玩宏。我将经常使用一些谓词,并希望以更短的方式编写它们。我不喜欢下一个状态在一系列行动结束时消失。我要的是正面和中间的信息。所以我更喜欢写这样的东西:

代码语言:javascript
复制
(case current-state
  [(state-1)
   (match symbol
     [:PRED-1: next-state
      (some-action)]
     [:PRED-2: next-state
      (some-action)]
     ; ...
     )]
  ; ...
  )

我对宏没有太大的经验,而且我早期的尝试都出了问题。我的第一次部分尝试只是谓词宏。下面是一个简单的例子:

代码语言:javascript
复制
(define (in-list value lst)
  (if (list? (member value lst))
    #true
    #false))
(define (is-non-zero-digit? symbol)
  (in-list symbol '(1 2 3 4 5 6 7 8 9)))
(define-syntax :NOT-0:
  #'(? is-non-zero-digit?))

(match 0
  [:NOT-0: 'wrong]
  [_ 'right])
; 'wrong

我不知道为什么会这样。我以为:NOT-0:会扩展到(? is-non-zero-digit?)。我尝试的另一件事是通过定义一个名为transition的宏来获得我想要的顺序。

代码语言:javascript
复制
; defined earlier in file
(define-syntax-rule
  (transition pattern next-state action ...)
  [pattern action ... next-state])
; ...
; the below is from a rackunit test
(define a-variable 0)
(define (side-effect)
  (set! a-variable 1))
(define result
  (match 0
    (transition (? is-non-zero-digit?) 'wrong (side-effect))
    [_ 'right]))
(check-equal? result 'right)
(check-equal? a-variable 1))

但是我得到了错误state-machine.rkt:220:21: ?: unbound identifier。我想要答案,为我提供一个方法,以获得所需的形式,并希望得到一个解释,为什么我以前的尝试没有成功。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-03-30 06:54:32

让我们先来谈谈为什么您的:NOT-0:不能工作。首先,宏是一个语法对象转换器。也就是说,从语法对象到语法对象的函数。所以你需要写:

代码语言:javascript
复制
(define-syntax :NOT-0:
  (lambda (stx) #'(? is-non-zero-digit?)))

或者用它的速记形式:

代码语言:javascript
复制
(define-syntax (:NOT-0: stx)
  #'(? is-non-zero-digit?))

但是,修正后的代码也不能很好地工作。原因是,在默认情况下,球拍宏被扩展为“expanded”。这意味着:

代码语言:javascript
复制
(define-syntax-rule (foo (#:foo x))
  x)

(define-syntax-rule (bar x)
  (#:foo x))

(foo (bar 1)) ; doesn't work, because `foo` is expanded first, and it couldn't find #:foo

大多数希望让用户像foo一样扩展其功能的宏将提供一个“宏定义宏”,您可以使用它来定义bar,而foo理解bar应该首先展开。有关技术细节,请参阅Matthew等人的协同工作的宏

针对您的特殊问题,Racket的match提供了define-match-expander,这是我前面描述的宏定义宏。你可以这样使用它:

代码语言:javascript
复制
(define-match-expander :NOT-0:
  ;; can also use syntax-case on stx to further ensure that stx must have a particular shape.
  (lambda (stx) #'(? is-non-zero-digit?)))

(define (is-non-zero-digit? symbol)
  ;; no need to define in-list. member alone would suffice
  (member symbol '(1 2 3 4 5 6 7 8 9)))

(match 0
  [(:NOT-0:) 'wrong]
  [_ 'right])

注意,您需要:NOT-0:周围的括号。如果您有一个裸的:NOT-0:match将把它当作要绑定匹配值的标识符。

就我个人而言,我不认为球拍的match适合这里。通常,当有许多(? predicate)子句时,它建议您将其转换为cond

代码语言:javascript
复制
(cond
  [(predicate-1? symbol) ...]
  [(predicate-2? symbol) ...]
  ...)

最后,您可以创建自己的match,如果您真的希望它是您想要的形式的话。你可以把你的match扩展到cond或者球拍的match。作为奖励,您将对其中的子窗体拥有完整的控制权,允许您交换“操作”和“状态”。下面是一个小例子。

代码语言:javascript
复制
(define-syntax-rule (match e [pred e*] ... [#:else e-else])
  (let ([v e]) ; so that we evaluate e only once
    (cond [(pred v) e*] ... [else e-else])))

(match 0
  [is-non-zero-digit? 'wrong]
  [#:else 'right])

(require (only-in racket/match [match r:match]))
;; Racket's match is still available via r:match
票数 3
EN

Stack Overflow用户

发布于 2020-05-21 04:43:29

按照@sorawee的建议,如果有人对我的最终结果感兴趣,我自己做了宏:

代码语言:javascript
复制
(define-syntax-rule 
  (transition to-evaluate
                 [pred next-symbol body ... ] ... 
                 [#:else else-next-symbol else-body ...])
  (let ([v to-evaluate])
    (cond 
      [(pred v) body ... next-symbol] ... 
      [else else-body ... else-next-symbol])))

它将转换如下的表达式:

代码语言:javascript
复制
(transition symbol
  [is-digit?  '1st-operand
    (send accumulator-register push symbol)]
  [is-operator?  'got-op
    (set! current-operator symbol)]
  [#:else (clear-calculator)])

代码语言:javascript
复制
(cond
  [(is-digit? symbol)
   (send accumulator-register push symbol)
   '1st-operand]
  [(is-operator? symbol)
    (set! current-operator symbol)
    'got-op]
  [else (clear-calculator)])
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60922883

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档