F#基础教程 模式匹配

      模式匹配允许你查看一个标识符,根据其值的不同做出不同的计算。这有点类似if … then … else …表达式,和C++ 或 C#中的switch 语句很相似,但它的功能更强大,更灵活。

      F#允许你在一个类型变量或值上进行模式匹配的构造。它有几种不同的形式和做法。在必要位置该语言有异常处理的语法,我们将在本章后面讨论“异常与异常处理”。模式匹配最简单的形式就是匹配值,在“值与函数”里,你已看到用它实现的Fibonacci数列。为说明语法,下一个例子将生成Lucas 序列,数字顺序如下:1, 3, 4, 7, 11, 18, 29, 47, 76, ….。Lucas 序列与Fibonacci 定义形式相同;只有出发点是不同的。

#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)
printfn "(luc 11) = %i" (luc 11)
printfn "(luc 12) = %i" (luc 12)
执行结果:
(luc 2) = 3
(luc 6) = 18
(luc 11) = 199
(luc 12) = 322

      这种模式匹配语法是在要匹配的标识符前面加关键字match,后跟关键字with,其次是所有可能的匹配规则用竖线(|)隔开。在最简单的情况下,一个规则由一个常量或一个标识符,后跟一个箭头(->),然后是匹配规则的值的表达式。在函数 luc的定义中,你可以看到,有两个分支是常量,值1和2,分别被替换为值1和3。第四个分支将匹配大于2的x值,这将导致进一步调用luc函数。

      该规则的匹配在顺序上依赖于它的定义,如果模式匹配不完整,编译器将给出错误,换句话说,可能有一些输入值不匹配任何规则,在luc函数里,如果你省略了最后的规则,任何大于2的x值将不能匹配任何规则。如果有一个规则永远不会被匹配,这通常是因为在他们前面有另一个更普遍的规则,编译器将发出一个警告。在luc函数里,如果第四条规则移动到第一个规则之前,因为第一条规则匹配任何x的值,其他规则将不会被匹配。

      你可以添加一个守卫(如例子中的第一条规则),精确的控制规则的触发。一个守卫由关键字when和一个布尔表达式组成。一旦规则被匹配,子句将求值,只有当表达式的值为true时,规则才触发。如果表达式的值为false,其余的规则将被匹配。第一条规则是函数的异常处理。规则的第一部分是一个标识符,将匹配任何整数,但守卫意味着规则将只匹配那些小于或等于零的整数。

      如果你需要,你可以省略第一个|。这将是一个非常有用的最小模式匹配,并且可以放在一行。你可以在下面的例子中看到,例中还演示了使用下划线(_) 做为通配符。_将匹配任何值,并且告诉编译器,你不需要使用该值。例如,在booleanToString 函数里,你不需要使用第二个规则中的常量true,因为如果第一条规则不匹配,你知道x的值将是true。此外,你不需要通过x推理出字符串"True",所以可以忽略该值,使用_通配符。

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

      模式匹配的另一个有用的特点是,你可以使用竖线(|)将两种模式组合成一个。下面的例子,stringToBoolean,表明了这一点。在第一条的两个规则中,你想要两个字符串,可以求出相同的值,而不是两个单独的规则,你只需使用|在两个模式之间。

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


printfn "(booleanToString true) = %s"
     (booleanToString true)
printfn "(booleanToString false) = %s"
     (booleanToString false)


printfn "(stringToBoolean \"True\") = %b"
     (stringToBoolean "True")
printfn "(stringToBoolean \"false\") = %b"
     (stringToBoolean "false")
printfn "(stringToBoolean \"Hello\") = %b"
     (stringToBoolean "Hello")

执行结果:

(booleanToString true) = True
(booleanToString false) = False
(stringToBoolean "True") = false
(stringToBoolean "false") = true
(stringToBoolean "Hello") = Microsoft.FSharp.FailureException: unexpected input
    at Prog.stringToBoolean(String x)
    at Prog._main()

      模式匹配也可以使用F#定义的类型。下面两个示例演示了针对元组的模式匹配,两个函数都使用模式匹配实现了布尔运算 "and” 和 "or”。每个采用的方式略有不同。

#light
let myOr b1 b2 =
     match b1, b2 with
     | true, _ -> true
     | _, true -> true
     | _ -> false
let myAnd p =
     match p with
     | true, true -> true
     | _ –> false

      myOr函数有两个布尔参数,它们用逗号分隔,组成的元组(tuple)做为关键字匹配。myAnd函数有一个参数,其本身就是一个元组。无论哪种方式,为元组创建的模式匹配语法基本相同,类似创建元组的语法。

      如果需要匹配元组内部的值,用逗号分割常量或标识符,标识符和常量的位置匹配元组内部的定义。如上例显示的关于myOr函数的第一条和第二条规则和关于myAnd的第一条规则。在这些规则中,用常量匹配了元组的一部份。你可以使用标识符,如果你想使用元组中的一部分进行计算,因为你的计算并不意味着要使用元组的所有部分。myOr的第三个规则和MyAnd的第二个规则显示了整个元组与_通配符的匹配。如果还要在规则中使用值可以用标识符替换掉规则的定义。

printfn "(myOr true false) = %b" (myOr true false)
printfn "(myOr false false) = %b" (myOr false false)
printfn "(myAnd (true, false)) = %b" (myAnd (true, false))
printfn "(myAnd (true, true)) = %b" (myAnd (true, true))

执行结果:

(myOr true false) = true
(myOr false false) = false
(myAnd (true, false)) = false
(myAnd (true, true)) = true

      F#里最常用的模式匹配是列表模式匹配;实际上,这是列表处理的首选方式。下面的例子改编”列表”一节,这一次使用了模式匹配而不是if … then … else …. 。为方便起见,原函数更名为concatListOrg。两者相比,很容易看出,使用模式匹配的版本大约只有一半的代码行,实践中更可取。模式语法循环提取列表头并使用连接列表的语法连接。这个模式由代表头部的标识符,然后是::,最后是代表列表其余部分的标识符组成。concatList 的第一个规则可以看到这一点。你还可以匹配常数列表,在concatList 的第二个规则中,可以看到一个空列表的匹配。

#light
let listOfList = [[2; 3; 5]; [7; 11; 13]; [17; 19; 23; 29]]


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
         []
let primes = concatList listOfList

print_any primes
执行结果:
[2; 3; 5; 7; 11; 13; 17; 19; 23; 29]

      从列表的头部开始处理,然后递归处理列表的尾部,是列表最常见的模式匹配方法,但它肯定不是唯一的列表模式匹配.下面的例子显示了一些功能组合的其他用途。第一个规则演示了如何匹配一个固定长度的列表,在此例中是包含三个项目的列表,在这里使用标识符获取项目的值,使其能够被打印到控制台。第二条规则查看列表的前三个项目,看他们是否是整数1,2,3序列,如果是则打印信息到控制台。最后两个规则是标准的列表头/尾处理,如果没有匹配前两个规则,则会使用后两个规则递归与返回。

#light
let rec findSequence l =
     match l with
     | [x; y; z] ->
         printfn "Last 3 numbers 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; 7; 8; 9; 8; 7; 6; 5; 4; 3; 2; 1]
findSequence testSequence
执行结果:
Found sequence 1, 2, 3 within the list
Last 3 numbers in the list were 3 2 1

      由于在模式匹配有这样一个共同的任务,在F#语言中提供了一种替代的简写语法。如果一个函数的唯一目标是模式匹配以上的东西,值的使用此语法。下个conactStringList 版本的模式匹配,使用关键字function,然后是模式函数的参数及表达式,最后是单独列出所有的其他规则。

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


let jabber = ["'Twas "; "brillig, "; "and "; "the "; "slithy "; "toves "; "..."]

let completJabber = conactStringList jabber
print_endline completJabber
执行结果:
'Twas brillig, and the slithy toves ...

      在F#里模式匹配还有其他的用途,但我还没有详细介绍使用模式匹配的其他类型。在下一节将有更多的信息。

转载于:https://www.cnblogs.com/IBBC/archive/2011/11/25/2263442.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值