列表的球拍模式匹配

时间:2017-11-14 23:11:48

标签: list pattern-matching racket ellipsis

我正在尝试与列表进行模式匹配,但出于某种原因,当我执行以下操作时,我会收到意外匹配:

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

问题:为什么codecode-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))

在这个例子中,我正在解析一个包含以下内容的markdown文件:

# Code Demo

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

我得到以下xexpr s:

1

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

2

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

然而,由于某些原因,这些都不匹配。

2 个答案:

答案 0 :(得分: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中[](){}相同{1}}。

修改

你仍然有点倒退。在这段代码中:

(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未被引用,它将匹配任何原子,并作为该子句中的变量绑定。它将与<{1}}使用变量同名的与绑定变量相关,它会创建它们。你返回变量。因此:

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)))) 滥用quasiquote到与创建列表结构完全不同的东西。它使用它们来匹配文字并将未分隔的符号捕获为变量。

答案 1 :(得分:1)

确保您清楚自己匹配的是什么。在Racket x表达式中,属性名称是符号,但值是字符串。因此,您匹配的表达式类似于(pre ([class "brush: js"])) ___) - 而不是 (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的问题的N / A.我提到它只是为了不会分散注意力和另一个嵌入式问题。:))