updated_at: 2022-02-27
说到Racket语言的match,其实它主是用于条件判断的,而谈到条件判断,在当代的编程语言中,最为大家熟知的莫过于if这个关键字了。当然Racket语言中也有if,但是其是最最基础的一个条件判断。其他诸如case、cond,亦或本文在后面即将铺展开来讲的match,尤其是match,在Racket语言的条件判断中,因为LISP语言高度同像性的S-expression语法,赋予其了非常多高阶使用方法。
1
从if谈起
很早时候,我就在官方文档读到过Choosing the Right Construct这个章节,但只是一略而过,认为没有什么特别大的价值。后来慢慢写的Racket代码多了,有几个关于let、if等几个索然无味的疑惑一直停留在脑海里,然后偶然重读了上面章节的文章才恍然大悟。
我在很长一段时间里面都在疑惑,Racket作为一个Scheme语言的流行方言,俨然已经是一个库非常丰富且成熟的语言了,为什么没有条件判断if关键字的否定判断关键字呢?因此在否定时都需要写成:
Like definitional constructs, conditionals come in many flavors, too. Because cond and its relatives (case, match, etc) now allow local uses of define, you should prefer them over if.
所以大体上文档是说,如果能不用
if,我们应该尽量避免它。所以这应该就是Racket语言中默认没有
if-not的原因之一,因为惯例上我们应该使用
case、
cond亦或
match:
> (case (+ 3 2) |
[(5) "result is 5"] |
[else "result is not 5"]) | |
"result is 5" |
> (cond |
[(= (+ 3 2) 5) "result is 5"] |
[else "result is not 5"]) | |
"result is 5" |
> (match (+ 3 2) |
[(== 5 =) "result is 5"] |
[_ "result is not 5"]) | |
eval:3:0: _: wildcard not allowed as an expression |
in: (_ "result is not 5") |
match在做条件判断时,其所能处理的维度远远胜过于if。这在上面提到的官方文档的Conditionals章节有更加明确的阐述和使用建议。
1.1
#:when/not/regexp在string对比中的使用
比如,下面一个正常的if条件判断语句:
> (require racket/string) |
> (define name "Yanying Wang") |
|
Your family name is Wang |
用match可以写成:
> (require racket/match |
racket/string) | |
> (define name "Yanying Wang") |
|
Your family name is Wang |
用match和正则表达则可以写成:
> (require racket/match |
racket/string) | |
> (define name "Yanying Wang") |
; 写法1: |
|
Your family name is Wang |
; 写法2: |
> (match name |
[(regexp #rx"(?<!Wang)$") |
(printf "Your family name is not Wang")] |
[_ (printf "Your family name is Wang")]) | |
Your family name is Wang |
1.2
app在判断list长度中的使用
又如,我们需要判断一个
list的长度是否是5,if语句写成:
> (define list1 '(1 4 8 9 5)) |
|
length of 5 list |
用match则写成:
> (require racket/match) |
> (define list1 '(1 4 8 9 5)) |
|
length of 5 list |
如上示例只是一些简单的条件判断,在我们要做更复杂的条件判断的时候,match的优势才会充分显示出来。
1.3
hash-table在hash键值对比中的使用
比如我们有一个HTTP的headers,其中有键值对Accept: application/json和Token: I am a racketeer,在Racket中以hasheq的数据结构存在,我们需要根据其值来做条件判断:
> (require racket/match) |
> (define headers (hasheq 'Accept "application/json" 'Token "I am a racketeer")) |
> (match headers |
[(hash-table ('Accept "application/json") ('Token "I am a racketeer")) (printf "correct Accept and Token detected")] |
[(hash-table ('Accept "text/html")) (printf "[Accept: text/html] detected")] |
[(hash-table ('Accept accept-value)) (printf "[Accept: ~a] detected" accept-value)] |
[_ (printf "unknown values")]) | |
correct Accept and Token detected |
> (match (hasheq 'Accept "text/html") |
[(hash-table ('Accept "application/json") ('Token "I am a racketeer")) (printf "correct Accept and Token detected")] |
[(hash-table ('Accept "text/html")) (printf "[Accept: text/html] detected")] |
[(hash-table ('Accept accept-value)) (printf "[Accept: ~a] detected" accept-value)] |
[_ (printf "unknown values")]) | |
[Accept: text/html] detected |
> (match (hasheq 'Accept "app/html") |
[(hash-table ('Accept "application/json") ('Token "I am a racketeer")) (printf "correct Accept and Token detected")] |
[(hash-table ('Accept "text/html")) (printf "[Accept: text/html] detected")] |
[(hash-table ('Accept accept-value)) (printf "[Accept: ~a] detected" accept-value)] |
[_ (printf "unknown values")]) | |
[Accept: app/html] detected |
> (match (hasheq 'abc "abc") |
[(hash-table ('Accept "application/json") ('Token "I am a racketeer")) (printf "correct Accept and Token detected")] |
[(hash-table ('Accept "text/html")) (printf "[Accept: text/html] detected")] |
[(hash-table ('Accept accept-value)) (printf "[Accept: ~a] detected" accept-value)] |
[_ (printf "unknown values")]) | |
unknown values |
2
示例:匹配一段字符串,相对应的去输出Xexpr进而转成HTML
我们需要从"大连市气象局发布大风红色预警[IV级/一般]"字符串中提取颜色,并且相对应的输出成HTML文档,比如是:<p class="sssubtext" style="color:Red">大连市气象局发布大风红色预警[IV级/一般]</p>。
2.1
使用cond的写法
> (require racket/match racket/string xml) |
|
> (gen-html "大连市气象局发布大风红色预警[IV级/一般]") |
"<p class=\"sssubtext\" style=\"color:Red\">大连市气象局发布大风红色预警[IV级/一般]</p>" |
2.2
使用match的#:when的写法
> (require racket/match racket/string xml) |
|
> (gen-html "大连市气象局发布大风红色预警[IV级/一般]") |
"<p class=\"sssubtext\" style=\"color:Red\">大连市气象局发布大风红色预警[IV级/一般]</p>" |
2.3
使用match的regexp的写法
> (require racket/match racket/string xml) |
> (define (gen-html str) |
(define color |
(match str |
[(regexp #rx".*蓝色.*") "color:Blue"] |
[(regexp #rx".*黄色.*") "color:Yellow"] |
[(regexp #rx".*橙色.*") "color:Oringe"] |
[(regexp #rx".*红色.*") "color:Red"] |
[_ "color:Black"])) |
(xexpr->string |
`(p ((class "sssubtext") (style ,color)) |
,str))) | |
> (gen-html "大连市气象局发布大风红色预警[IV级/一般]") |
"<p class=\"sssubtext\" style=\"color:Red\">大连市气象局发布大风红色预警[IV级/一般]</p>" |
2.4
使用match的app的写法
> (require racket/match racket/string xml) |
|
> (gen-html "大连市气象局发布大风红色预警[IV级/一般]") |
"<p class=\"sssubtext\" style=\"color:Red\">大连市气象局发布大风红色预警[IV级/一般]</p>" |