我想编写一个与输入参数的不同语法匹配的函数,例如
(define (foo e)
(match e
[int (print "int")]
[(and _ _) (printf "and")])
)
这样我可以用:
来调用它(foo 1) --> prints "int"
(foo (and #t #f)) --> prints "and"
但这似乎不起作用。我做错了什么?
答案 0 :(得分:2)
对此没有简短而完整的回应。
首先,你不能用程序做到这一点。您可以使用谓词来确定某些内容是否为某个数字,例如number?
,但您无法使用此and
来捕获foo
之类的语法。
但真正非常非常重要的一点是你无法捕捉到这样的事情:当e
看到(and #t (+ 3 4))
时,扩展器已经扩展了任何语法并评估了表达式。也就是说,例如,(if #t (+ 3 4) #f)
已转换为类似e
的表达式,并且在您在过程中看到e
时已经计算了此表达式,在这种情况下7
只是quote
。在Racket中,在调用过程之前评估过程的参数。这意味着,大致应用了所有语法变换器,并评估了生成的扩展表达式,然后在结果值上调用过程。
如果您只关心修改s表达式,可以(quote (and 2 3)
参数并使用结果列表。 'and
将为您提供包含符号define-syntax
的列表,而不是语法,因此这可能不是您想要的。我提到它有些完整。
在Racket中管理问题的方法是使用宏。同样,问题是在调用过程时,作为参数传递给过程的表达式已经已经被评估,因此您必须在#lang racket/base
(require (for-syntax racket/base syntax/parse))
(define-syntax (foo stx)
(syntax-parse stx
#:literals (and)
[(_ i:number)
(syntax (displayln "int"))]
[(_ (and a b))
(syntax (displayln "and"))]))
级别工作才能捕获表达式在他们扩展和评估之前。也就是说,要解决您的特定问题,您需要类似于将某些语法作为参数并返回语法的过程:
stx
此过程采用一些语法并产生一些其他语法。语法参数(and #t #f)
(可以命名为任何东西,它只是约定)必须匹配一个子句,否则它是一个语法错误。当您定义语法时,您实际上是在创建一种新的编程语言,这是该语言的新构造。如果匹配,例如案例#'(foo (and #t #f))
,则会将#'(displayln "and")
替换为{{1}}。在评估之前,它实际上不会显示“和”。
要开始使用宏,请参阅the Racket Guide或the Racket Reference。
答案 1 :(得分:0)
问题是匹配模式int
匹配所有内容。
> (match 42
[int "foo"]
[_ "bar"])
"foo"
如果您想匹配符号int
,则需要撰写'int
> (match 'int
['int "foo"]
[_ "bar"])
"foo"
> (match 42
['int "foo"]
[_ "bar"])
"bar"
另请注意,在您的示例中:(foo (and #t #f))
在调用函数之前计算函数调用的参数。也就是说,首先使用(and #t #f)
评估#f
作为结果。然后foo
被调用:(foo #f)
。
如果您想使用列表和符号表示法调用foo
,请使用:(foo '(and #t #f))
。