我的儿子和我正在一起学习球拍,正在构建一个非常简单的基于文本的冒险,可以直接从REPL中使用。因此,例如,播放器可以键入(go 'north)
或(take 'apple)
。
在得到一些基本的东西工作之后,我的儿子认为引用名词有点痛苦(奇怪的是,那些parens不会打扰他!),所以我们用宏攻击了一下,我们确实得到了一些工作,但它需要一个明确的功能和相应的宏,例如
(define (do-take item) ...)
(define-syntax (take stx)
(define item (cadr (syntax->datum stx)))
(datum->syntax stx `(do-take ',item)))
我认为我们可以做得比这更好,所以我再读一遍并想出这个:
(require (for-syntax racket/syntax))
(define-syntax (define-verb stx)
(syntax-case stx ()
[(_ (verb noun) body-first body-rest ...)
(with-syntax ([verb-fun (format-id stx "do-~a" #'verb)])
#'(begin
(define-syntax-rule (verb noun) (verb-fun 'noun))
(define (verb-fun noun) body-first body-rest ...)))]))
现在,我们可以写(define-verb (take item) ...)
,REPL的播放器可以输入(take apple)
。
我的问题是,鉴于我们想要实现的目标,这是一种合理的方法,还是有更简单/惯用的方法来实现同样的目标?
答案 0 :(得分:1)
一般来说,我建议做的主要是使用syntax/parse
库。它有更多用于解析语法的工具。您甚至可以使用define-syntax-parser
之类的表单来使您的宏更加简洁。使用syntax/parse
重写代码(删除那一行,因为它似乎没有做任何事情),你的宏看起来像这样:
#lang racket
(require syntax/parse/define
(for-syntax syntax/parse racket/syntax))
(define-syntax-parser define-verb
[(_ (verb:id noun) body ...+)
(define/syntax-parse verb-fun (format-id stx "do-~a" #'verb))
#'(begin
(define-simple-macro (verb noun) (verb-fun 'noun))
(define (verb-fun noun) body ...))])
这比你给出的例子提供了一些好处:
:id
确保verb
是文字标识符,而不是表达式。...+
表示您只需要一个body
模式,而不是两个。define/syntax-parse
表示您的代码缩进的次数不会超过with-syntax
。 (虽然这是一个偏好的问题。)