Chapter3 - Pattern Match

模式匹配 (Pattern Matching)

 

模式匹配允许你通过一些定义的标识符的值来执行不同的算法步骤,他有点像之前的

if ... then ... else,也可能跟类似 C++ 跟 C# 的 switch,但更加强大。

 

下面我们来看一个例子:

 


#light

let rec luc x =
    
match x with
    
| x when x <= 0 -> failwith "value must be greater than 0"
    
| 1 -> 1
    
| 2 -> 3
    
| x -> luc (x - 1) + luc(--x - 2)


 

printfn "(luc 2) = %i" (luc 2)

printfn "(luc 6) = %i" (luc 6)

 

结果为

(luc 2) = 3

(luc 6) = 18

 

首先我们来介绍以下luc,他跟之前将的斐波拉契数列很像,唯一不同的只有起始值。

like 1,3,4,7,11,18,29....

然后是解释,我们传递了一个参数 x ,然后match x with 表示我们要将 x 与以下的集中模式进行匹配,

各个模式间用 | 进行分割。

如第一次使用2为参数进行调用,因为他会直接匹配到2,所以最后的结果是3.

第二次,我们使用6,什么都不会匹配到,所以他到了 x -> 这里,这里表示任意的x都会匹配到。

当然,前面的第一个匹配模式已经决定了 x 是大于 0 的数。

在第一个模式里,你可以看到有一个限制,这里称之为护卫(Guard),用 when 来限制,

当 x 小于等于 0 的时候,他会匹配到第一个模式,然后抛出异常。

 

下面的另外一个例子则表示了,我们可以讲模式的匹配写到一行了,而 _ 则表示匹配任意的值。

 


#light

let booleanToString x =
    
match x with false -> "False" | _ -> "True"


下面是另一种比较有用的用法,你可以用 | 来连接两个不同的模式,让他们返回相同的东西。

 


#light

let stringToBoolean x =
    
match x with
    
| "True" | "true" -> true
    
| "False" | "false" -> false
    
| _ -> failwith "unexpected input"

 
printfn "(booleanToString \"true\") = %b" (stringToBoolean "true")
printfn "(bolleanToString \"Hello\") = %b" (stringToBoolean "Hello")


 

结果:

(booleanToString "true") = true

 

第二个因为前面的都匹配不到,所以由最后一个 _ 匹配到并抛出异常。

接下来的一个 “或” 跟 “与”的例子则展示了如何同时匹配多个值。

 


#light

let myOr b1 b2 =
    
match b1,b2 with
    
| true, _ -> true
    
| _, true -> true
    
| _ -> false

 
let myAnd p =
    
match p with
    
| truetrue -> true
    
| _ -> false


 

或即是当传递的两个条件,只要有一个为真,结果就为真,否则为假。

与即是只有当传递的两个条件都为真是,结果才为真。这里有一个有趣的地方,也就是 “与”的那里,

编译器在这里能够自动的推断出 p 为一个元组,并把它拆开来进行模式匹配。

 

另外一个用法,也是比较推荐的用法,就是用模式匹配来处理列表(list)。

 


let listOfList = [[1,2,3], [4,5,6], [7,8]]

let rec concatList l =
    
match l with
    
| head :: tail -> head @ (concatList tail)
    
| [] -> []

let rec concatListOrg l =
    
if List.nonempty l then
        
let head = List.hd l in
        
let tail = List.tl l in
        head @ (concatListOrg tail)
    
else
        []


 

上面的两个函数的效果是一样的,但我们能发现,使用模式匹配的话,代码会显得简介很多。

这是得益于模式匹配推断出 l 是一个列表,他能够自动的帮我们把列表进行分割,head代表列表的第一个元素,

tail 则表示余下的元素,当传入的列表能够进行分割的时候,就把第一个元素拿出来,然后递归的调用函数,

并将第一个元素跟函数下一次调用的结果进行链接。

 

let primes = concatList listOfList

 

结果:

[1,2,3,4,5,6,7,8]

 

模式匹配还能够对列表进行复杂的匹配,我们来看下一个例子。

 


let rec findSequence l =
    
match l with
    
| [x; y; z] ->
        printfn "Last 3 member in the list were %i %i %i"
        x y z
    
| 1 :: 2 :: 3 :: tail ->
        printfn "Found sequence 1,2,3 within the list"
        findSequence tail
    
| head :: tail -> findSequence tail
    
| [] -> ()

 


我们先来看结果再解释。

 

let testSequence = [1; 2; 3; 4; 5; 6; 3; 2; 1]

findSequence testSequence

-----------------------

Found sequence 1,2,3 within the list

Last 3 numbers in the list were 3 2 1

-----------------------

 

首先解释第一个模式:当匹配的列表只有3个元素的时候匹配。并把剩余的3个元素按顺序赋予 x y z,

第二个模式 : 当列表以 1 2 3 开头的时候匹配,并将剩余的元素作为参数再次调用findSequence

第三个模式 :分割列表为第一个头元素与剩余元素,并将剩余的元素作为参数再次调用findSequence

最后一个 : 当列表为空,则结束。

当第一次传递testSequence进去的时候,匹配到了第二个模式,因为testSequence是以1 2 3 开头的列表

然后把剩下的元素继续作为参数调用函数,则相当于 findSequence [4;5;6;3;2;1]

这次调用会匹配到第三个模式,他将列表拆分后再次调用,直到列表只剩下3个元素,

则匹配到第一个模式,输出最后的三个元素。

 

最后一个例子是可以使用 function 关键字来取消在定义函数时需要的参数定义:

 


let rec conactStringList =
    
function head :: tail -> head + conactStringList tail
            
| [] -> ""


posted on 2010-09-20 08:33  兴说:  阅读(1857)  评论(0编辑  收藏  举报