【问题标题】:Racket pattern matching of lists列表的球拍模式匹配
【发布时间】:2023-03-21 03:10:01
【问题描述】:

我正在尝试对列表进行模式匹配,但由于某种原因,当我执行以下操作时,我得到了意外的匹配:

> (define code '(h1 ((id an-id-here)) Some text here))
> (define code-match-expr '(pre ([class brush: python]) ...))
> (match code
    [code-match-expr #t]
    [_ #f])
#t

问题:为什么code 匹配code-match-expr

实际用例

我在 Racket REPL 中尝试过这个,因为我实际上想解决另一个实际问题:使用 Pollen 的 pygments 包装函数来突出显示代码,稍后将输出为 HTML。为此,我编写了以下代码,出现问题的地方:

(define (read-post-from-file path)
  (Post-from-content (replace-code-xexprs (parse-markdown path))))

(define (replace-code-xexprs list-of-xexprs)
  ;; define known languages
  (define KNOWN-LANGUAGE-SYMBOLS
    (list 'python
          'racket
          'html
          'css
          'javascript
          'erlang
          'rust))
  ;; check if it matches for a single language's match expression
  ;; if it mathces any language, return that language's name as a symbol
  (define (get-matching-language an-xexpr)
    (define (matches-lang-match-expr? an-xexpr lang-symbol)
      (display "XEXPR:") (displayln an-xexpr)
      (match an-xexpr
        [`(pre ([class brush: ,lang-symbol]) (code () ,more ...)) lang-symbol]
        [`(pre ([class brush: ,lang-symbol]) ,more ...) lang-symbol]
        [_ #f]))

    (ormap (lambda (lang-symbol)
             ;; (display "trying to match ")
             ;; (display an-xexpr)
             ;; (display " against ")
             ;; (displayln lang-symbol)
             (matches-lang-match-expr? an-xexpr lang-symbol))
           KNOWN-LANGUAGE-SYMBOLS))

  ;; replace code in an xexpr with highlightable code
  ;; TODO: What happens if the code is in a lower level of the xexpr?
  (define (replace-code-in-single-xexpr an-xexpr)
    (let ([matching-language (get-matching-language an-xexpr)])
      (cond [matching-language (code-highlight an-xexpr matching-language)]
            [else an-xexpr])))

  ;; apply the check to all xexpr
  (map replace-code-in-single-xexpr list-of-xexprs))

(define (code-highlight language code)
  (highlight language code))

在本例中,我正在解析一个包含以下内容的降价文件:

# Code Demo

```python
def hello():
    print("Hello World!")
```

我得到以下xexprs:

1.

(h1 ((id code-demo)) Code Demo)

2.

(pre ((class brush: python)) (code () def hello():
    print("Hello World!")))

但是,由于某种原因,这些都不匹配。

【问题讨论】:

    标签: list pattern-matching racket ellipsis


    【解决方案1】:

    match 是语法,不评估模式。由于code-match-expr 是一个符号,它将整个表达式(评估code 的结果)绑定到变量code-match-expr 并在模式匹配时评估其余的表达式。结果将始终为#t

    请注意,第二个模式,符号_,是相同的模式。它也匹配整个表达式,但_ 的特殊之处在于它不像code-match-expr 那样被绑定。

    永远不要使用您定义的变量code-match-expr,这一点很重要,但由于match 绑定了一个同名变量,您的原始绑定将在match 的结果中被隐藏。

    按您的预期工作的代码可能如下所示:

    (define (test code)
      (match code 
        [`(pre ([class brush: python]) ,more ...) #t]
        [_ #f]))
    
    (test '(h1 ((id an-id-here)) Some text here))
    ; ==> #f
    
    (test '(pre ((class brush: python))))
    ; ==> #t
    
    (test '(pre ((class brush: python)) a b c))
    ; ==> #t
    

    如您所见,,more ... 模式表示零或多个,因为在 Racket 中,[](){} 相同,因此忽略了哪种括号。

    编辑

    你还是有点倒退了。在这段代码中:

    (define (matches-lang-match-expr? an-xexpr lang-symbol)
      (display "XEXPR:") (displayln an-xexpr)
      (match an-xexpr
        [`(pre ([class brush: ,lang-symbol]) (code () ,more ...)) lang-symbol]
        [`(pre ([class brush: ,lang-symbol]) ,more ...) lang-symbol]
        [_ #f]))
    

    当一个模式被创建时,由于lang-symbol 没有被引用,它将匹配 anything 原子并作为该子句中的变量绑定到它。它与match 同名的绑定变量没有任何关系 不使用变量,而是创建它们。你返回变量。因此:

    (matches-lang-match-expr? '(pre ([class brush: jiffy]) bla bla bla) 'ignored-argument)
    ; ==> jiffy
    

    这是你想做的事情:

     (define (get-matching-language an-xexpr)
        (define (get-language an-xexpr)
          (match an-xexpr
            [`(pre ([class brush: ,lang-symbol]) (code () ,more ...)) lang-symbol]
            [`(pre ([class brush: ,lang-symbol]) ,more ...) lang-symbol]
            [_ #f]))
        (let* ((matched-lang-symbol (get-language an-xexpr))
               (in-known-languages (memq matched-lang-symbol KNOWN-LANGUAGE-SYMBOLS)))
          (and in-known-languages (car in-known-languages))))
    

    再次......match 滥用 quasiquote 到与创建列表结构完全不同的东西。它使用它们来匹配文字并将未引用的符号捕获为变量。

    【讨论】:

    • 我对此有疑问:(1) 当我在 Racket 外壳中输入 `(pre ([class brush: python]) 1) 时,它会打印出 '(pre ((class brush: python)) 1)。那是因为表达式被评估了吗?否则,反引号似乎只引入了另一个列表,我不明白为什么反引号会创建除单引号之类的列表之外的其他内容。 (2) 例如,我如何将 ````` 表达式存储在 hash 中? Racket 告诉我 more 在我尝试这样做时没有定义。我想在比赛中使用more 会导致它被绑定并且没有错误,但我有很多。
    • @Zelphir 这是#lang racket 可视化值的疯狂方式。它不是显示它变成的值,而是显示一个将计算为该值的表达式。所以`(some thing) 计算为(some thing),但球拍显示'(some thing),因为如果你计算它应该成为相同的值(some thing),它当然仍将显示为'(some thing)。 quasiquote ` ' 没有区别,除非您使用 unquote (,)。尝试评估 `(a b ,(+ 1 2)) 然后再次评估。
    • 您的代码在 Racket REPL 中对我有用,但是当我尝试将其放入代码中时,不知何故,我仍然总是匹配或总是错误,这取决于我如何尝试。你能看一下吗?我会将其作为实际用例的编辑放入问题中。如果您认为我应该提出一个新问题,我会接受这个作为答案。
    • @Zelphir 只需将其添加到您问题的底部作为编辑,我会查看它。
    • 我有多种编程语言需要匹配,我用,lang-symbol 填写它们,而不是在那里写python,其中lang-symbol(list python racket css) 中的一个元素例如。我将代码添加到问题中,也许这只是我忽略的东西。
    【解决方案2】:

    确保你清楚你匹配的是什么。在 Racket x 表达式中,属性名称是符号,但值是字符串。因此,您匹配的表达式将类似于 (pre ([class "brush: js"])) ___) -- not (pre ([class brush: js]) ___)

    要匹配该字符串并提取"brush: " 之后的部分,您可以使用pregexp 匹配模式。 Here is a snippet that Frog uses to extract the language to give to Pygments:

    (for/list ([x xs])
      (match x
        [(or `(pre ([class ,brush]) (code () ,(? string? texts) ...))
             `(pre ([class ,brush]) ,(? string? texts) ...))
         (match brush
           [(pregexp "\\s*brush:\\s*(.+?)\\s*$" (list _ lang))
            `(div ([class ,(str "brush: " lang)])
                  ,@(pygmentize (apply string-append texts) lang
                                #:python-executable python-executable
                                #:line-numbers? line-numbers?
                                #:css-class css-class))]
           [_ `(pre ,@texts)])]
        [x x])))
    

    (这里pygmentize 是在其他 Frog 源代码中定义的函数;它是将 Pygments 作为一个单独的进程运行并在它之间传递文本的包装器。但是你可以用另一种方式来使用 Pygments 或任何其他语法荧光笔。那是您关于match 的问题不适用。我提到它只是为了不会分散注意力和另一个嵌入式问题。:))

    【讨论】:

    • 这个答案更正了我错误的匹配大小写表达式,并展示了如何进一步处理匹配的语言,但我只能接受一个答案。尽管如此 +1 :)
    猜你喜欢
    • 2014-07-06
    • 2020-06-19
    • 1970-01-01
    • 2016-07-13
    • 2016-02-17
    • 2011-10-27
    • 2019-05-02
    • 2021-09-14
    • 1970-01-01
    相关资源
    最近更新 更多