【问题标题】:Runtime pattern matching in RacketRacket 中的运行时模式匹配
【发布时间】:2015-02-28 16:45:42
【问题描述】:

如果 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 questionRyan 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)

模式匹配现在是否在运行时发生?这是个坏主意吗?

【问题讨论】:

  • 起初您描述希望match 成为一个函数——但关于在运行时更改子句的部分似乎并不是您最初的、您想要它的根本原因。您能否详细说明您希望 match 成为函数的原始/主要原因? (也许如果我们了解您想要做什么,我们可以提出更多想法?)

标签: macros pattern-matching racket


【解决方案1】:

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

关键是:

  1. 为编译时间定义my-clauses(“用于语法”)。

  2. 在宏模板中正确引用。

所以:

(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】:

    在上一个示例中,模式匹配发生在运行时。

    一种检查方法是查看扩展:

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

    【讨论】:

      【解决方案3】:

      非常感谢你们,你们的回答让我深思。我正在尝试做的事情还不是很明确,但我似乎在这个过程中学到了很多东西,所以这很好。

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

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

      无论如何,今天我开始阅读有关宏、评估上下文和阶段的内容。我越来越喜欢球拍了,我仍在研究如何制作语言......我现在将切换到修补模式,看看我是否能在我的头爆炸之前得到一些我所描述的工作的基本形式想法。

      【讨论】:

        猜你喜欢
        • 2020-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-26
        • 1970-01-01
        相关资源
        最近更新 更多