12 Pattern Matching
(require racket/match) is needed for #lang racket/base.
match 形式支持对任意 Racket 值进行模式匹配,这不同于像 regexp-match 这样将正则表达式与字节和字符序列进行比较的函数(参见 Regular Expressions)。
(match target-expr [pattern expr ...+] ...)
match 形式获取 target-expr 的结果,并按顺序尝试匹配每个 pattern。一旦找到匹配,就对相应的 expr 序列求值以获得 match 表达式的结果。如果 pattern 包含模式变量,则它们被视为通配符,每个变量在 expr 中绑定到它所匹配的输入片段。
大多数 Racket 字面表达式可以用作模式:
> (match 2 [1 'one] [2 'two] [3 'three]) 'two
> (match #f [#t 'yes] [#f 'no]) 'no
> (match "apple" ['apple 'symbol] ["apple" 'string] [#f 'boolean]) 'string
像 cons、list 和 vector 这样的构造器可以用来创建匹配 pair、list 和 vector 的模式:
> (match '(1 2) [(list 0 1) 'one] [(list 1 2) 'two]) 'two
> (match '(1 . 2) [(list 1 2) 'list] [(cons 1 2) 'pair]) 'pair
> (match #(1 2) [(list 1 2) 'list] [(vector 1 2) 'vector]) 'vector
用 struct 绑定的构造器也可以用作模式构造器:
> (struct shoe (size color)) > (struct hat (size style))
> (match (hat 23 'bowler) [(shoe 10 'white) "bottom"] [(hat 23 'bowler) "top"]) "top"
模式中不带引号的、非构造器的标识符是模式变量,它们在结果表达式中被绑定,但 _ 除外,它不进行绑定(因此通常用作通配符):
> (match '(1) [(list x) (+ x 1)] [(list x y) (+ x y)]) 2
> (match '(1 2) [(list x) (+ x 1)] [(list x y) (+ x y)]) 3
> (match (hat 23 'bowler) [(shoe sz col) sz] [(hat sz stl) sz]) 23
> (match (hat 11 'cowboy) [(shoe sz 'black) 'a-good-shoe] [(hat sz 'bowler) 'a-good-hat] [_ 'something-else]) 'something-else
请注意,标识符 else 不是一个保留的通配符(像 _ 那样)。如果 else 出现在模式中,那么它来自 racket/base 的绑定可能会被遮蔽,这会导致 cond 和 case 出现问题。
> (match 1 [else (case 2 [(a 1 b) 3] [else 4])]) eval:15:0: case: bad syntax (not a datum sequence)
expected: a datum sequence or the binding 'else' from
racket/base
given: a locally bound identifier
at: else
in: (case 2 ((a 1 b) 3) (else 4))
> (match #f [else (cond [#f 'not-evaluated] [else 'also-not-evaluated])])
要匹配绑定到标识符的值,请使用 ==。
> (define val 42)
> (match (list 42) [(list (== val)) 'matched]) 'matched
> (match (list 43) [(list (== val)) 'not-matched] [_ 'this-branch-is-evaluated]) 'this-branch-is-evaluated
> (match (list 43) [(list val) ; without ==, val is a pattern variable (format "match binds val to ~a" val)]) "match binds val to 43"
省略号写作 ...,在 list 或 vector 模式中的作用类似于 Kleene 星号:前面的子模式可以用于匹配任意次数的连续元素。如果省略号后面的子模式包含模式变量,则该变量会匹配多次,并在结果表达式中绑定到一个匹配结果列表:
> (match '(1 1 1) [(list 1 ...) 'ones] [_ 'other]) 'ones
> (match '(1 1 2) [(list 1 ...) 'ones] [_ 'other]) 'other
> (match '(1 2 3 4) [(list 1 x ... 4) x]) '(2 3)
> (match (list (hat 23 'bowler) (hat 22 'pork-pie)) [(list (hat sz styl) ...) (apply + sz)]) 45
省略号可以嵌套以匹配嵌套的重复,在这种情况下,模式变量可以绑定到匹配结果的嵌套列表:
> (match '((! 1) (! 2 2) (! 3 3 3)) [(list (list '! x ...) ...) x]) '((1) (2 2) (3 3 3))
quasiquote 形式(有关更多信息请参见 Quasiquoting: quasiquote and ‘)也可以用来构建模式。在普通的 quasiquote 形式中,不带引号的部分意味着正常的 Racket 求值,但在这里,不带引号的部分表示回到正常的模式匹配。
因此,在下面的示例中,with 表达式是模式,它被重写为应用表达式,第一个实例中使用 quasiquote 作为模式,第二个实例中使用 quasiquote 来构建表达式。
> (match `{with {x 1} {+ x 1}} [`{with {,id ,rhs} ,body} `{{lambda {,id} ,body} ,rhs}]) '((lambda (x) (+ x 1)) 1)
有关更多模式形式的信息,请参见 racket/match。
像 match-let 和 match-lambda 这样的形式在原本必须是标识符的位置支持模式。例如,match-let 将 let 推广为解构绑定:
> (match-let ([(list x y z) '(1 2 3)]) (list z y x)) '(3 2 1)
有关这些附加形式的信息,请参见 racket/match。
(part ("(lib scribblings/reference/reference.scrbl)" "match")) in (part ("(lib scribblings/reference/reference.scrbl)" "top")) provides more on pattern matching.