如果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表达式)给出的函数 .
所以我想'd have to use eval, but the naive way won'工作因为 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)
模式匹配现在是在运行时发生的吗?这是个坏主意吗?
3 回答
模式匹配在最后一个示例中在运行时发生 .
检查的一种方法是查看扩展:
是否是个好主意......
使用eval相当慢,所以如果你经常调用它,最好找到另一个解决方案 . 如果你还没有看过它,你可能想在Racket博客上阅读"On eval in dynamic languages generally and in Racket specifically." .
要回答问题的第一部分(假设您不一定需要在运行时提供
match
子句):关键是:
为编译时定义
my-clauses
("for syntax") .在宏模板中正确引用 .
所以:
非常感谢你们,你的回答给了我很多思考 . 我想要做的仍然没有很好地定义,但我似乎在这个过程中学到了很多,所以这很好 .
最初的想法是制作一个方程编辑器,它是paredit和计算机代数系统之间的混合体 . 输入初始数学s表达式,例如
(+ x (* 2 y) (^ (- y x) 2)
. 之后,程序会为您提供一个步骤转换列表,您通常可以手工制作:替换变量,分配,因子等 . 像CAS一样,但一次只能一步 . 当用户按下相应的键组合时,将执行转换,尽管一种可能性是仅显示一堆可能的结果,并让用户在其中选择表达式的新状态 . 对于UI charterm现在会做 .起初我以为我会在转换表达式中将转换作为子句,但现在我认为我将使它们成为接受和返回s表达式的函数 . 选择编译时间与运行时的麻烦在于我希望用户能够添加更多转换,并选择自己的键绑定 . 这可能意味着他们在编译应用程序之前编写了一些我需要的代码,或者他们需要我的代码,因此它不会强迫我使用eval . 但最好是我给用户一个REPL,这样他就可以对表达式进行编程控制以及与之交互 .
无论如何,今天我读到了有关宏,评估环境和阶段的内容 . 我越来越喜欢球拍而且我还在调查制作语言......现在我将切换到修补模式,看看我是否得到了一些我正在描述的工作的基本形式想法 .