【问题标题】:racket syntax pattern with multiple ...s具有多个 ...s 的球拍语法模式
【发布时间】:2021-01-15 17:12:28
【问题描述】:

我正在研究使用类似于 unix 的管道的球拍语法,如下所示:

> ("FOO" > string-replace "O" "E" > string-append "x" > string-downcase)
"feex"

这是一个蛮力解决方案,它支持带有 2、1 和 0(额外)参数的过程:

(require (prefix-in racket/base/ racket/base) syntax/parse/define)
(define-syntax-parser #%app
  [(_ data (~literal >) proc a b (~literal >) rest ...) #'(#%app (proc data a b) > rest ...)]
  [(_ data (~literal >) proc a (~literal >) rest ...) #'(#%app (proc data a) > rest ...)]
  [(_ data (~literal >) proc (~literal >) rest ...) #'(#%app (proc data) > rest ...)]
  [(_ data (~literal >) proc rest ...) #'(#%app proc data rest ...)]
  [(_ rest ...) #'(racket/base/#%app rest ...)])

问题在于寻找下一个管道,因为语法模式不允许多个 ... 模式。宏需要知道下一个管道在哪里关闭第一个管道的表单。除非有办法用不匹配的括号构建部分语法对象?

我可以嵌套省略号,但是我必须使用额外的括号:

(define-syntax-parser #%app
  [(_ data (~literal >) (proc params ...) > rest ...) #'(#%app (proc data params ...) > rest ...)]
  [(_ data (~literal >) proc rest ...) #'(#%app proc data rest ...)]
  [(_ rest ...) #'(racket/base/#%app rest ...)])

> ("FOO" > (string-replace "O" "E") > (string-append "x") > string-downcase)
"feex"

没有多余的括号有什么办法吗?

我知道 clojure 的线程宏,但如果你必须嵌套它们,它们就很难遵循。

编辑:这个问题的解决方案现在可以通过racket packagegithub 获得

【问题讨论】:

    标签: racket pointfree tacit-programming


    【解决方案1】:

    您可以将~seq 模式与省略号结合使用来匹配不带括号的内容。例如:

    (define-syntax-parser split
      [(_ (~seq a b) ...)
       #'(list a ... b ...))
    

    将要求split 提供偶数个参数,然后将其重新排列并构建到一个列表中:

    (split 1 2 3 4 5 6)
    ; =>
    (list 1 3 5 2 4 6)
    

    当然,请注意~seq 并不神奇,并且在解析时对回溯的支持有限。但原则上您应该能够执行以下操作:

    (data (~seq (~literal >) proc args ...) ...)
    

    【讨论】:

      【解决方案2】:

      @Leif Andersen 的建议在 args 模式不允许 > 时效果最佳,因此在 args 上添加 (~not (~literal >)) 可能会有所帮助:

      (_ data (~seq (~literal >) proc (~and args (~not (~literal >))) ...) ...)
      

      【讨论】:

        【解决方案3】:

        结合@Lief Andersen 和@Alex Knauth 的答案,我想出了这个解决方案。据我所知,没有办法避免递归,但也许语法/解析中有一些我不知道的魔法。

        (require (prefix-in base/ racket/base) syntax/parse/define)
        (define-syntax-parser #%app
          [(_ data (~seq (~literal >) proc (~and args (~not (~literal >))) ...)) #'(base/#%app proc data args ...)]
          [(_ data (~seq (~literal >) proc (~and args (~not (~literal >))) ...) rest ...) #'(#%app (proc data args ...) rest ...)]
          [(_ rest ...) #'(base/#%app rest ...)])
        

        示例用法(注意第一个>是repl提示符,不是代码):

         > ("FOO" > string-downcase > string-replace "o" "e" > string-append "abc")
         "feeabc"
        

        添加对管道数据到过程最后一个参数的支持也非常简单:

        (require (prefix-in base/ racket/base) syntax/parse/define)
        (define-syntax-parser #%app
          [(_ data (~seq (~literal >) proc (~and args (~not (~literal >)) (~not (~literal >>))) ...)) #'(base/#%app proc data args ...)]
          [(_ data (~seq (~literal >>) proc (~and args (~not (~literal >)) (~not (~literal >>))) ...)) #'(base/#%app proc args ... data)]
          [(_ data (~seq (~literal >) proc (~and args (~not (~literal >)) (~not (~literal >>))) ...) rest ...) #'(#%app (proc data args ...) rest ...)]
          [(_ data (~seq (~literal >>) proc (~and args (~not (~literal >)) (~not (~literal >>))) ...) rest ...) #'(#%app (proc args ... data) rest ...)]
          [(_ rest ...) #'(base/#%app rest ...)])
        

        示例用法:

        > ('(1 2 3) > first > * 3 >> list-ref '(a b c d e f))
        'd
        

        我会对 cme​​ts 感兴趣,了解此宏如何影响性能。也许在代码而不是模式中进行转换会更好。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-10
          • 2018-07-04
          • 1970-01-01
          • 2023-03-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多