如果Racket的match
宏是一个函数,我可以这样做:
(define my-clauses (list '[(list '+ x y) (list '+ y x)]
'[_ 42]))
(on-user-input
(λ (user-input)
(define expr (get-form-from-user-input user-input)) ; expr could be '(+ 1 2), for example.
(apply match expr my-clauses)))
我认为有两种截然不同的方式。一个是将my-clauses
移动到宏观世界,并制作一个像这样的宏(不起作用):
(define my-clauses (list '[(list '+ x y) (list '+ y x)]
'[_ 42]))
(define-syntax-rule (match-clauses expr)
(match expr my-clauses)) ; this is not the way it's done.
; "Macros that work together" discusses this ideas, right? I'll be reading that today.
(on-user-input
(λ (user-input)
(define expr (get-form-from-user-input user-input)) ; expr could be '(+ 1 2), for example.
(match-clauses expr)))
替代方案,最终可能会更好,因为它允许我在运行时更改my-clauses
,将以某种方式在运行时执行模式匹配。有什么办法可以在运行时值上使用匹配吗?
In this question Ryan Culpepper说
在不使用
eval
的情况下,无法创建将形式参数和正文作为运行时值(S表达式)给出的函数。
所以我想我必须使用eval,但天真的方式不会起作用,因为match
是一个宏
(eval `(match ,expr ,@my-clauses) (current-namespace))
我从导游
获得了以下伏都教所需的结果(define my-clauses '([(list'+ x y) (list '+ y x)]
[_ 42]))
(define-namespace-anchor a)
(define ns (namespace-anchor->namespace a))
(eval `(match '(+ 1 2) ,@my-clauses) ns) ; '(+ 2 1)
模式匹配现在是否在运行时发生?这是个坏主意吗?
答案 0 :(得分:2)
回答问题的第一部分(假设您不一定需要在运行时提供match
子句):
关键是:
为编译时定义my-clauses
(“为语法”)。
在宏模板中正确引用。
所以:
(begin-for-syntax
(define my-clauses (list '[(list '+ x y) (list '+ y x)]
'[_ 42])))
(define-syntax (match-clauses stx)
(syntax-case stx ()
[(_ expr) #`(match expr #,@my-clauses)]))
答案 1 :(得分:1)
模式匹配在最后一个例子中在运行时发生。
检查的一种方法是查看扩展:
> (syntax->datum
(expand '(eval `(match '(+ 1 2) ,@my-clauses) ns)))
'(#%app eval (#%app list* 'match ''(+ 1 2) my-clauses) ns)
是否是一个好主意......
使用eval相当慢,所以如果你经常调用它,最好找到另一个解决方案。如果您还没有看过它,可能需要阅读Racket博客上的"On eval in dynamic languages generally and in Racket specifically."。
答案 2 :(得分:1)
非常感谢你们,你的回答给了我很多思考。我想做的事情仍然没有很好地定义,但我似乎在这个过程中学到了很多东西,所以这很好。
最初的想法是制作一个方程编辑器,它是paredit和计算机代数系统之间的混合体。输入初始数学s表达式,例如(+ x (* 2 y) (^ (- y x) 2)
。之后,程序会为您提供一个步骤转换列表,您通常可以手工制作:替换变量,分配,因子等。像CAS一样,但一次只能一步。当用户按下相应的键组合时,将执行转换,尽管一种可能性是仅显示一堆可能的结果,并让用户在其中选择表达式的新状态。对于用户界面charterm目前也会这样做。
起初我以为我会在转换表达式中将转换作为子句,但现在我认为我将使它们成为获取和返回s表达式的函数。选择编译时间与运行时的麻烦在于我希望用户能够添加更多转换,并选择自己的键绑定。这可能意味着他们在编译应用程序之前编写了一些我需要的代码,或者他们需要我的代码,所以它并没有强迫我使用eval。但最好是我给用户一个REPL,这样他就可以对表达式进行编程控制以及与它的交互。
无论如何,今天我读到了有关宏,评估环境和阶段的内容。我越来越喜欢球拍了,我还在调查制作语言......现在我将切换到修补模式,看看我是否得到了一些基本形式的我所描述的工作方式在我的脑袋爆炸出新的想法之前。