F# 活动模式
最后修改于 2025 年 5 月 3 日
在本文中,我们将探讨 F# 中的活动模式。活动模式使得模式匹配的能力超越了标准的可辨识联合,功能强大且灵活。
一个活动模式是 F# 中一个强大的特性,它允许开发者定义自定义规则来分解和处理数据,从而扩展了传统的模式匹配。与主要处理内置类型和预定义结构的标准模式匹配不同,活动模式能够以灵活且特定于领域的方式来解释值。这使得它们在处理复杂转换、结构化数据提取以及处理非统一类型的同时保持类型安全方面特别有用。
活动模式有几种形式:
- 单例模式 (Single-case patterns):通过匹配单一形式的输入来简化数据提取。
- 多例模式 (Multi-case patterns):允许多个不同的分支,实现更具表达力的数据分解。
- 部分模式 (Partial patterns):处理可能匹配成功也可能不成功的情况,通常与选项类型一起使用。
- 参数化模式 (Parameterized patterns):接受额外的参数,使其能够适应动态的模式匹配场景。
F# 单例活动模式
单例活动模式将输入数据转换为另一种形式。
single_case.fsx
let (|ToUpper|) (s: string) = s.ToUpper() let greet name = match name with | ToUpper upper -> printfn "HELLO, %s!" upper greet "John" greet "Alice"
我们创建一个简单的活动模式,将字符串转换为大写。
let (|ToUpper|) (s: string) = s.ToUpper()
定义一个转换其输入的单例活动模式。
λ dotnet fsi single_case.fsx HELLO, JOHN! HELLO, ALICE!
F# 多例活动模式
多例活动模式将输入数据划分成多种可能性。
multi_case.fsx
let (|Even|Odd|) n = if n % 2 = 0 then Even else Odd let testNumber x = match x with | Even -> printfn "%d is even" x | Odd -> printfn "%d is odd" x testNumber 4 testNumber 7
我们定义一个活动模式,将数字分类为偶数或奇数。
let (|Even|Odd|) n =
该多例活动模式将输入分为两类。
λ dotnet fsi multi_case.fsx 4 is even 7 is odd
F# 部分活动模式
部分活动模式仅匹配某些输入,返回选项类型。
partial.fsx
let (|Integer|_|) (s: string) = match System.Int32.TryParse(s) with | true, n -> Some n | _ -> None let parseInput input = match input with | Integer n -> printfn "Got integer: %d" n | _ -> printfn "Not an integer" parseInput "42" parseInput "hello"
展示一个用于将字符串解析为整数的部分活动模式。
let (|Integer|_|) (s: string) =
|_|
表示这是一个可能匹配失败的部分活动模式。
λ dotnet fsi partial.fsx Got integer: 42 Not an integer
F# 参数化活动模式
活动模式除了匹配的值之外,还可以接受额外的参数。
parameterized.fsx
let (|DivisibleBy|_|) divisor n = if n % divisor = 0 then Some DivisibleBy else None let fizzbuzz n = match n with | DivisibleBy 15 _ -> "FizzBuzz" | DivisibleBy 3 _ -> "Fizz" | DivisibleBy 5 _ -> "Buzz" | _ -> string n [1..20] |> List.map fizzbuzz |> List.iter (printfn "%s")
用 FizzBuzz 问题演示参数化活动模式。
let (|DivisibleBy|_|) divisor n =
该活动模式同时接受一个除数参数和要匹配的值。
λ dotnet fsi parameterized.fsx 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz
用于类型测试的 F# 活动模式
活动模式可以简化类型测试和转换。
type_test.fsx
let (|IsString|IsInt|IsBool|Other|) (obj: obj) = match obj with | :? string as s -> IsString s | :? int as i -> IsInt i | :? bool as b -> IsBool b | _ -> Other let describe obj = match obj with | IsString s -> printfn "String: %s" s | IsInt i -> printfn "Int: %d" i | IsBool b -> printfn "Bool: %b" b | Other -> printfn "Unknown type" describe (box "hello") describe (box 42) describe (box true) describe (box 3.14)
展示活动模式如何清晰地处理运行时类型测试。
match obj with | :? string as s -> IsString s
该活动模式封装了类型测试和转换的逻辑。
λ dotnet fsi type_test.fsx String: hello Int: 42 Bool: true Unknown type
用于 XML 解析的 F# 活动模式
活动模式可以简化处理复杂数据结构的工作。
xml.fsx
open System.Xml.Linq let (|Elem|_|) name (el: XElement) = if el.Name.LocalName = name then Some (el.Elements()) else None let (|Attr|_|) name (el: XElement) = match el.Attribute(XName.Get name) with | null -> None | attr -> Some attr.Value let parseBook (el: XElement) = match el with | Elem "book" children -> children |> Seq.iter (fun child -> match child with | Elem "title" _ -> printfn "Title: %s" (child.Value) | Elem "author" _ -> printfn "Author: %s" (child.Value) | Elem "price" _ -> printfn "Price: %s" (child.Value) | _ -> printfn "Unknown element") | _ -> printfn "Not a book element" let xml = """ <books> <book> <title>F# in Depth</title> <author>John Smith</author> <price>45.99</price> </book> </books> """ let doc = XDocument.Parse(xml) doc.Root.Elements() |> Seq.iter parseBook
演示如何使用活动模式来解析和处理 XML 数据。
let (|Elem|_|) name (el: XElement) =
活动模式使 XML 处理代码更具声明性且更易读。
λ dotnet fsi xml.fsx Title: F# in Depth Author: John Smith Price: 45.99
F# 活动模式的组合
活动模式可以组合在一起以实现更复杂的匹配。
composition.fsx
let (|Positive|_|) n = if n > 0 then Some Positive else None let (|Negative|_|) n = if n < 0 then Some Negative else None let (|Zero|_|) n = if n = 0 then Some Zero else None let (|Even|Odd|) n = if n % 2 = 0 then Even else Odd let describeNumber n = match n with | Positive & Even -> printfn "%d is positive and even" n | Positive & Odd -> printfn "%d is positive and odd" n | Negative & Even -> printfn "%d is negative and even" n | Negative & Odd -> printfn "%d is negative and odd" n | Zero -> printfn "Zero" describeNumber 4 describeNumber 7 describeNumber -2 describeNumber -3 describeNumber 0
展示如何使用逻辑与 (&) 运算符组合多个活动模式。
Positive & Even
组合两个活动模式来匹配既是正数又是偶数的数字。
λ dotnet fsi composition.fsx 4 is positive and even 7 is positive and odd -2 is negative and even -3 is negative and odd Zero
在本文中,我们探讨了 F# 活动模式的强大功能和灵活性。它们提供了一种将模式匹配扩展到几乎任何场景的方法,同时保持代码的整洁和声明性。