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.