我正在尝试为使用制作的计算器编写某种类似于有限状态机的东西racket/gui
,因此我决定使用case
和match
实现它。对于特定的状态和符号,我将执行一些任意代码并返回机器的下一个状态。一个简单的例子:
(case current-state
[(state-1)
(match symbol
[(? predicate-1?)
(some-action)
next-state]
[(? predicate-2?)
(some-action)
next-state]
; ...
)]
; ...
)
不过,我想使它更易于阅读,并想通过宏来玩弄。我经常使用某些谓词,并希望以较短的方式编写它们。而且我不喜欢在一系列操作结束时丢失下一个状态。我想要这些信息放在首位。所以我更喜欢写这样的东西:
(case current-state
[(state-1)
(match symbol
[:PRED-1: next-state
(some-action)]
[:PRED-2: next-state
(some-action)]
; ...
)]
; ...
)
我对宏的使用不太了解,而我的早期尝试都变得很糟糕。我的第一个部分尝试只是谓词宏。这是一个简单的例子:
(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
:
; 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
。我希望得到答案,以便为我提供一种获取所需表格的方式,并且希望您能解释一下为什么我以前的尝试没有奏效。
让我们先说说为什么你:NOT-0:
不工作。首先,宏是语法对象转换器。即,从语法对象到语法对象的功能。所以你需要写:
(define-syntax :NOT-0:
(lambda (stx) #'(? is-non-zero-digit?)))
或使用其简写形式:
(define-syntax (:NOT-0: stx)
#'(? is-non-zero-digit?))
但是更正后的代码也不起作用。原因是Racket宏默认情况下是“从外向内”扩展的。这意味着:
(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 Flatt等人的可协同工作的宏。
对于您的特定问题,Racketmatch
提供define-match-expander
了一个宏,它定义了我上面描述的宏。您可以像这样使用它:
(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
:
(cond
[(predicate-1? symbol) ...]
[(predicate-2? symbol) ...]
...)
最后,match
如果您确实希望它以所需的形式出现,则可以创建自己的表单。然后,您可以根据需要扩展match
到cond
或球拍match
。另外,您将完全控制其中的子表单,从而可以交换“动作”和“状态”。这是一个小例子。
(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
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句