F# 中的主动模式
也许以前你就听说过主动模式----通常与"了不起的"或者"惊人的"这些词联系在一起. 主动模式是F# 众多特有功能中的一个,并且,当你对这些功能有所体验时,你会觉得主动模式将会是最强大的功能中的一个.这篇文章将会从主动模式的单条件和多条件匹配到部分和参数化匹配来展示它的强大之处.你会通过这篇文章里的一个例子来了解为什么它会命名为主动模式.那么,让我们开始吧.
首先,我们来认识一下主动模式. 主动模式是由被称为"Banana Clips" 的"(|" " |)"符号组成。主动模式 有多种不同的形式----在这篇文章中,我们将一一介绍它们。
单条件主动模式
主动模式中最简单的一种就是单条件匹配模式。它是为把输入转化为其它某种不同格式而设计的。一个简单的情况就是当你希望把一个字符串转化为一个全大写的版本,这时,你可以使用此方法。当然,你可以使用一个常规的函数来解决此情况中的问题,主动模式可以让你在一个let或者match 结构中将这些结果作为一部分使用。
下面是一些例子:
// Single case Active Patterns - For converting data
let (|UpperCase|) (x:string) = x.ToUpper()
// The item being matched is the parameter into the active pattern, and what comes
// after the ActivePattern name is the result. In this case the pattern match will
// only fire if the AP result is exactly "FOO".
let result = match "foo" with
| UpperCase "FOO" -> true
| _ -> false
assert (result = true)
// This function simply returns the upper case version of the parameter. (Since
// the AP result was bound to the identifier 'result'.)
let ucName name = match name with UpperCase result -> result
// An example of converting a string to a color.
let (|ToColor|) x =
match x with
| "red" -> System.Drawing.Color.Red
| "blue" -> System.Drawing.Color.Blue
| "white" -> System.Drawing.Color.White
| _ -> failwith "Unknown Color"
// Use AP in let binding
let form = new System.Windows.Forms.Form()
let (ToColor col) = "red"
form.BackColor <- col
多条件主动模式
相比较而言,多条件主动模式会更有意思。理解这些最好的方法就是:将整个输入分解成一些不同的东西。 例如,将所有的整数划分为偶数和奇数。
let (|Odd|Even|) x = if x % 2 = 0 then Even else Odd
let isDivisibleByTwo x = match x with Even -> true | Odd -> false
// This active pattern divides all strings into their various meanings.
let (|Pharagraph|Sentence|Word|WhiteSpace|) (input : string) =
let input = input.Trim()
if input = "" then
WhiteSpace
elif input.IndexOf(".") <> -1 then
// Notice that Pharagraph contains an tuple of sentence counts, and sentences.
let sentences = input.Split([|"."|], StringSplitOptions.None)
Pharagraph (sentences.Length, sentences)
elif input.IndexOf(" ") <> -1 then
// Notice that Sentence contains an Array of strings
Sentence (input.Split([|" "|], StringSplitOptions.None))
else
// Notice that the word contains a string
Word (input)
let rec countLetters str =
match str with
| WhiteSpace -> 0
| Word x -> x.Length
| Sentence words
-> Array.map countLetters words |> Array.fold (+) 0
| Pharagraph (_, sentences)
-> Array.map countLetters sentences |> Array.fold (+) 0
部分主动模式
有时候输入太大了以至于不能以单条件模式来完整地划分。在这种情况下你可以使用部分主动模式。总之,这些模式并不总是有返回值。最好的方法是考虑在这些能创造并能描述输入的一些分段。所以对所有的自然数中,只有少数可以认为是完美的正方形或被七整除数。
// Partial Active Patterns - For carving out a subsection of the input space.
let (|DivisibleBySeven|_|) input = if input % 7 = 0 then Some() else None
let (|IsPerfectSquare|_|) (input : int) =
let sqrt = int (Math.Sqrt(float input))
if sqrt * sqrt = input then
Some()
else
None
let describeNumber x =
match x with
| DivisibleBySeven & IsPerfectSquare -> printfn "x is divisible by 7 and is a perfect square."
| DivisibleBySeven -> printfn "x is divisible by seven"
| IsPerfectSquare -> printfn "x is a perfect square"
| _ -> printfn "x looks normal."
参数化的主动模式
到目前为止都很好了。但如果你的主动模式需要额外的信息呢?参数化的主动模式是一种简单的有额外参数的主动模似。值得注意的是在匹配语句里,只有最后被提到的"东西"才是主动模式的返回值或输出结果。其他所有的"东西"都是参数并被输入到主动模式。
在这个例子中,我们定义了一个部分主动模式来匹配输入是不是一个多参数。
// Parameterized Active Patterns - Passing Patameters into Active Patterns
let (|MultipleOf|_|) x input = if input % x = 0 then Some(input / x) else None
let factorize x =
let rec factorizeRec n i =
let sqrt = int (Math.Sqrt(float n))
if i > sqrt then
[]
else
match n with
| MultipleOf i timesXdividesIntoI
-> i :: timesXdividesIntoI :: (factorizeRec n (i + 1))
| _ -> factorizeRec n (i + 1)
factorizeRec x 1
assert([1; 10; 2; 5] = (factorize 10))
这样我们就有一些简单的主动模式例子,但我知道你们中许多人都好奇实际地你会用这些模式做什么?这问题的答案将会在下片文章中,我将会展示怎样用主动模式来简化正则表达式、XML解析和网页抓取。
原文链接:http://blogs.msdn.com/b/chrsmith/archive/2008/02/21/introduction-to-f_2300_-active-patterns.aspx