首页 文章

Racket中的运行时模式匹配

提问于
浏览
1

如果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 回答

  • 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." .

  • 1

    要回答问题的第一部分(假设您不一定需要在运行时提供 match 子句):

    关键是:

    • 为编译时定义 my-clauses ("for syntax") .

    • 在宏模板中正确引用 .

    所以:

    (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)]))
    
  • 2

    非常感谢你们,你的回答给了我很多思考 . 我想要做的仍然没有很好地定义,但我似乎在这个过程中学到了很多,所以这很好 .

    最初的想法是制作一个方程编辑器,它是paredit和计算机代数系统之间的混合体 . 输入初始数学s表达式,例如 (+ x (* 2 y) (^ (- y x) 2) . 之后,程序会为您提供一个步骤转换列表,您通常可以手工制作:替换变量,分配,因子等 . 像CAS一样,但一次只能一步 . 当用户按下相应的键组合时,将执行转换,尽管一种可能性是仅显示一堆可能的结果,并让用户在其中选择表达式的新状态 . 对于UI charterm现在会做 .

    起初我以为我会在转换表达式中将转换作为子句,但现在我认为我将使它们成为接受和返回s表达式的函数 . 选择编译时间与运行时的麻烦在于我希望用户能够添加更多转换,并选择自己的键绑定 . 这可能意味着他们在编译应用程序之前编写了一些我需要的代码,或者他们需要我的代码,因此它不会强迫我使用eval . 但最好是我给用户一个REPL,这样他就可以对表达式进行编程控制以及与之交互 .

    无论如何,今天我读到了有关宏,评估环境和阶段的内容 . 我越来越喜欢球拍而且我还在调查制作语言......现在我将切换到修补模式,看看我是否得到了一些我正在描述的工作的基本形式想法 .

相关问题