我正在尝试与列表进行模式匹配,但出于某种原因,当我执行以下操作时,我会收到意外匹配:
> (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))
在这个例子中,我正在解析一个包含以下内容的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!")))
然而,由于某些原因,这些都不匹配。
答案 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.我提到它只是为了不会分散注意力和另一个嵌入式问题。:))