如何使用模式匹配的结果在匹配扩展器中生成值?

How can I use the results of a pattern match to produce a value in a match-expander?

racket/match 模式中,我想匹配一些值,然后创建一个包装它们的结构。例如,采用以下(设计的)代码:

(struct foo (a b c))

(define (foo-from-string str)
  (match str
    [(pregexp #px"^(.+)\s(.+)\s(.+)$" (list _ a b c))
     (foo a b c)]
    [_ #f]))

我经常发现自己在其他模式中匹配这个包含三个元素的列表,然后从中创建一个结构。因此,我想通过编写自定义匹配扩展器来简化这一点。理想情况下,它会像这样工作:

(struct foo (a b c))

(define (foo-from-str str)
  (match str
    [(foo-string value) value]
    [_ #f]))

也就是说,它应该自动匹配满足正则表达式的字符串,然后在成功时将值存储到 foo 结构中并将其绑定到 value。我尝试编写如下内容来实现此目的:

(define-match-expander foo-string
  (λ (stx)
    (syntax-case stx ()
      [(_ result)
       #'(and (pregexp #px"^(.+)\s(.+)\s(.+)$" (list _ a b c))
              (app (λ (v) (foo a b c)) result))])))

不幸的是,这失败了,因为 abc 在调用传递给 app 模式的函数时未绑定。有什么方法可以实现这样的匹配扩展器,以便它可以对匹配值执行一些任意过程吗?

首先,您的 pregexp 格式有错字:

(pregexp #px"^(.+)\s(.+)\s(.+)$" a b c)

我想你想要:

(pregexp #px"^(.+)\s(.+)\s(.+)$" (list _ a b c))

至于你的主要目标:

I frequently find myself matching this list of three elements in other patterns, then creating a struct from it. Therefore, I wanted to simplify this....

好吧,您可以通过使用 app 和合适的函数来完成此操作。有趣的是,您的 foo-from-string 函数是...正是那个合适的函数。

例如,要匹配一个字符串并得到一个 foo 结构:

(match "a b c"
  [(app foo-from-string x) x])
;;=> (foo "a" "b" "c")

或者要匹配字符串并获取 foo 字段,请提供 foo 结构模式:

(match "a b c"
  [(app foo-from-string (foo a b c)) (list a b c)])
;;=> '("a" "b" "c")

诚然,我没有回答您有关匹配扩展器的问题,因为我对它们的了解还不够深入。所以我建议你也许不需要它们?


更新:实际上这似乎也是对匹配扩展器的回答:

(define-match-expander foo-string
  (λ (stx)
    (syntax-case stx ()
      [(_ x)
       #'(app foo-from-string x)])))

是的,app 正是您想要的。您只需要在 app.

中做更多
#lang racket

(struct foo (a b c) #:transparent)

(define-match-expander foo-string
  (λ (stx)
    (syntax-case stx ()
      [(_ result)
       #'(app (λ (v) (apply (λ (_ a b c) (foo a b c))
                            (regexp-match #px"^(.+)\s(.+)\s(.+)$" v)))
              result)])))

(define (foo-from-str str)
  (match str
    [(foo-string value) value]
    [_ #f]))

(foo-from-str "1 2 3")