F# 匹配表达式
最后修改日期:2025 年 5 月 17 日
本文将探讨如何在 F# 中有效使用匹配表达式来增强分支逻辑和数据处理。
匹配表达式通过将表达式与预定义的模式集进行比较,实现结构化分支。每个可能的输出称为一个分支,允许开发人员高效地处理数据。匹配表达式可用于轻松地转换输入、分解复杂数据结构或提取特定元素。
F# 匹配表达式依赖于 match
、with
和 when
关键字。match
关键字启动模式匹配过程,with
定义可能的匹配分支,when
引入额外的条件过滤器来细化匹配。这种语法提供了一种简洁而富有表现力的方式来处理条件逻辑,而无需过多的嵌套语句。
匹配常量模式
基本模式是常量、整数、字符串或枚举。
let word = "falcon" let langs = [ "Slovak" "German" "Hungarian" "Russian" "French" ] for lang in langs do let res = match lang with | "Slovak" -> "sokol" | "German" -> "Falke" | "Hungarian" -> "sólyom" | "Russian" -> "сокол" | "French" -> "faucon" | _ -> "unknown" printfn $"{word} in {lang} is {res}"
该程序打印出“猎鹰”一词在几种语言中的翻译。
let res = match lang with | "Slovak" -> "sokol" | "German" -> "Falke" | "Hungarian" -> "sólyom" | "Russian" -> "сокол" | "French" -> "faucon" | _ -> "unknown"
匹配表达式会返回值。匹配表达式中的每个分支都以 |
开头。字符串模式跟在 |
字符后面。返回值在 ->
之后指定。通配符匹配 _
为未识别的选项返回值。
λ dotnet fsi main.fsx falcon in Slovak is sokol falcon in German is Falke falcon in Hungarian is sólyom falcon in Russian is сокол falcon in French is faucon
匹配枚举
在下一个示例中,我们匹配枚举。
open System type Day = | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday let days = [ Monday; Tuesday; Wednesday; Thursday; Friday; Saturday; Sunday ] let rnd = new Random() let res = days |> Seq.sortBy (fun _ -> rnd.Next()) |> Seq.take 3 for e in res do match e with | Monday -> printfn "%s" "monday" | Tuesday -> printfn "%s" "tuesday" | Wednesday -> printfn "%s" "wednesay" | Thursday -> printfn "%s" "thursday" | Friday -> printfn "%s" "friday" | Saturday -> printfn "%s" "saturday" | Sunday -> printfn "%s" "sunday"
我们定义了一个 Day
枚举。我们随机选择三个 Day 枚举。
match e with | Monday -> printfn "%s" "monday" | Tuesday -> printfn "%s" "tuesday" | Wednesday -> printfn "%s" "wednesay" | Thursday -> printfn "%s" "thursday" | Friday -> printfn "%s" "friday" | Saturday -> printfn "%s" "saturday" | Sunday -> printfn "%s" "sunday"
在此匹配表达式中,我们匹配枚举。在这里我们不返回值;我们打印消息。
λ dotnet fsi main.fsx monday friday tuesday
F# 匹配守卫
守卫是必须在分支内满足的条件。守卫是通过 when
创建的。
let vals = [ 1; -3; 5; 6; 0; 4; -9; 11; 22; -7 ] for wal in vals do match wal with | n when n < 0 -> printfn "%d is negative" n | n when n > 0 -> printfn "%d is positive" n | _ -> printfn "zero"
通过模式匹配,我们将值分类为负值、正值和零值。
| _ -> printfn "zero"
通过这一点,我们创建了一个详尽的匹配。
λ dotnet fsi main.fsx 1 is positive -3 is negative 5 is positive 6 is positive zero 4 is positive -9 is negative 11 is positive 22 is positive -7 is negative
F# 匹配多个选项
可以使用 |
组合多个选项。
let grades = ["A"; "B"; "C"; "D"; "E"; "F"; "FX"] for grade in grades do match grade with | "A" | "B" | "C" | "D" | "E" | "F" -> printfn "%s" "passed" | _ -> printfn "%s" "failed"
我们将分数分为两类:及格和不及格。及格分支使用 |
组合所有匹配的值。
λ dotnet fsi main.fsx passed passed passed passed passed passed failed
F# 匹配记录
在下一个示例中,我们匹配记录。
type User = { FirstName: string LastName: string Occupation: string } let users = [ { FirstName = "John" LastName = "Doe" Occupation = "gardener" } { FirstName = "Jane" LastName = "Doe" Occupation = "teacher" } { FirstName = "Roger" LastName = "Roe" Occupation = "driver" } ] for user in users do match user with | { LastName = "Doe" } -> printfn "%A" user | _ -> ()
我们有几个用户,它们是使用记录创建的。通过记录模式匹配,我们选择了所有 Doce。
match user with | { LastName = "Doe" } -> printfn "%A" user | _ -> ()
我们将一个带有属性的匿名记录指定为模式。
λ dotnet fsi main.fsx { FirstName = "John" LastName = "Doe" Occupation = "gardener" } { FirstName = "Jane" LastName = "Doe" Occupation = "teacher" }
F# 匹配类型
使用 :?
,我们可以匹配类型。
open System.Collections type User = { FirstName: string LastName: string Occupation: string } let vals = new ArrayList() vals.Add(1.2) vals.Add(22) vals.Add(true) vals.Add("falcon") vals.Add( { FirstName = "John" LastName = "Doe" Occupation = "gardener" } ) for wal in vals do match wal with | :? int -> printfn "an integer" | :? float -> printfn "a float" | :? bool -> printfn "a boolean" | :? User -> printfn "a User" | _ -> ()
我们有一个包含不同数据类型的列表。我们遍历列表并打印每个元素的类型。
λ dotnet fsi main.fsx a float an integer a boolean a User
函数语法
当在函数中定义模式匹配时,可以使用函数关键字对其进行简化。
open System type Choices = | A | B | C let getVal = function | 1 -> A | 2 -> B | _ -> C let chx = [ for _ in 1..7 do yield getVal (Random().Next(1, 4)) ] printfn "%A" chx
在示例中,我们随机构建了一个选择列表。
let getVal = function | 1 -> A | 2 -> B | _ -> C
这是函数内部简化的模式匹配语法。
λ dotnet fsi main.fsx [B; C; A; C; A; B; B]
F# 匹配列表模式
在下一个示例中,我们匹配列表模式。
let vals = [ [ 1; 2; 3 ] [ 1; 2 ] [ 3; 4 ] [ 8; 8 ] [ 0 ] ] let twoels (sub: int list) = match sub with | [ x; y ] -> printfn "%A" [ x; y ] | _ -> () for sub in vals do twoels sub
该程序打印所有包含两个元素的列表。
λ dotnet fsi main.fsx [1; 2] [3; 4] [8; 8]
列表推导式
匹配模式可以用于列表推导式。
let res = [ for e in 1..100 do match e with | e when e % 3 = 0 -> yield "fizz" | e when e % 5 = 0 -> yield "buzz" | e when e % 15 = 0 -> yield "fizzbuzz" | _ -> yield (string e) ] printfn "%A" res
我们使用列表推导式解决了 fizz-buzz 挑战。所有值都存储在列表中。
λ dotnet fsi main.fsx ["1"; "2"; "fizz"; "4"; "buzz"; "fizz"; "7"; "8"; "fizz"; "buzz"; "11"; "fizz"; "13"; "14"; "fizz"; "16"; "17"; "fizz"; "19"; "buzz"; "fizz"; "22"; "23"; "fizz"; "buzz"; "26"; "fizz"; "28"; "29"; "fizz"; "31"; "32"; "fizz"; "34"; "buzz"; "fizz"; "37"; "38"; "fizz"; "buzz"; "41"; "fizz"; "43"; "44"; "fizz"; "46"; "47"; "fizz"; "49"; "buzz"; "fizz"; "52"; "53"; "fizz"; "buzz"; "56"; "fizz"; "58"; "59"; "fizz"; "61"; "62"; "fizz"; "64"; "buzz"; "fizz"; "67"; "68"; "fizz"; "buzz"; "71"; "fizz"; "73"; "74"; "fizz"; "76"; "77"; "fizz"; "79"; "buzz"; "fizz"; "82"; "83"; "fizz"; "buzz"; "86"; "fizz"; "88"; "89"; "fizz"; "91"; "92"; "fizz"; "94"; "buzz"; "fizz"; "97"; "98"; "fizz"; "buzz"]
活动模式
通过活动模式,我们可以定义输入数据的命名分区,并在模式匹配表达式中使用这些名称。
let (|Even|Odd|) input = if input % 2 = 0 then Even else Odd let testnum input = match input with | Even -> printfn "%d is even" input | Odd -> printfn "%d is odd" input testnum 3 testnum 8 testnum 11
在活动模式语法中,我们定义了 Even
和 Odd
名称。这些名称稍后在匹配表达式中使用。
let (|Even|Odd|) input = if input % 2 = 0 then Even else Odd
我们定义了 Even
和 Odd
活动模式。
let testnum input = match input with | Even -> printfn "%d is even" input | Odd -> printfn "%d is odd" input
这些名称在匹配表达式中使用。
λ dotnet fsi main.fsx 3 is odd 8 is even 11 is odd
正则表达式
我们可以在匹配模式中使用正则表达式。
open System.Text.RegularExpressions let (|RegEx|_|) p i = let m = Regex.Match(i, p) if m.Success then Some m.Groups else None let checkrgx (msg) = match msg with | RegEx @"\d+" g -> printfn "Digit: %A" g | RegEx @"\w+" g -> printfn "Word : %A" g | _ -> printfn "Not recognized" checkrgx "an old falcon" checkrgx "1984" checkrgx "3 hawks"
在示例中,我们在模式匹配中使用活动模式和正则表达式。
λ dotnet fsi main.fsx Word : seq [an] Digit: seq [1984] Digit: seq [3]
异常处理
匹配表达式可用于处理异常。
open System printf "Enter a number: " let value = Console.ReadLine() let n = match Int32.TryParse value with | true, num -> num | _ -> failwithf "'%s' is not an integer" value let f = function | value when value > 0 -> printfn "positive value" | value when value = 0 -> printfn "zero" | value when value < 0 -> printfn "negative value" | _ -> () f n
我们期望用户输入一个整数值。我们尝试解析输入值;如果不是整数,我们将以错误消息失败。
let n = match Int32.TryParse value with | true, num -> num | _ -> failwithf "'%s' is not an integer" value
如果解析成功,TryParse
返回 true。因此,我们在第一个分支中有 true 布尔模式。对于其余的,我们使用 failwithf
失败。
匹配选项类型
选项类型用于表示可能存在也可能不存在的值。模式匹配是 F# 中处理 Some
和 None
情况的安全且符合习惯的方式。
let describeOption opt = match opt with | Some v -> printfn "The value is %d" v | None -> printfn "No value found" describeOption (Some 10) describeOption None
在此示例中,函数 describeOption
在值存在时打印该值,否则打印一条消息。对选项类型进行模式匹配可以使代码简洁且安全。
λ dotnet fsi main.fsx The value is 10 No value found
在本文中,我们研究了 F# 中的匹配表达式。