Давайте сначала поговорим о том, почему ваш :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 и др.
. Для вашей конкретной проблемы match
Racket предоставляет 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
или Racket 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