F# 记录
最后修改时间 2025 年 5 月 1 日
在本文中,我们将探讨如何在 F# 中有效使用记录并理解它们在数据结构中的作用。
一个 记录 是将命名的值组合在一个结构化格式中。默认情况下,记录是不可变的,这意味着它们的值在初始化后不能被更改。然而,它们可以包含成员,如函数或计算属性,从而增强其功能。如果需要可变性,可以使用 mutable
关键字将字段显式标记为可变。
F# 记录简单示例
记录使用 type
关键字定义。值在 { }
括号之间指定。
type User = { FirstName: string; LastName: string; Occupation: string; Salary: int } let users = [ { FirstName = "Robert"; LastName = "Novak"; Occupation = "teacher"; Salary = 1770 } { FirstName = "John"; LastName = "Doe"; Occupation = "gardener"; Salary = 1230 } { FirstName = "Lucy"; LastName = "Novak"; Occupation = "accountant"; Salary = 670 } ] users |> List.iter (printfn "%A")
该程序定义了一个 User
记录。我们从记录类型创建了一个包含三个用户的列表。然后迭代该列表。
type User = { FirstName: string; LastName: string; Occupation: string; Salary: int }
记录类型定义了三个字段。字段之间用分号分隔。这些分号是可选的。字段名与其类型之间用冒号分隔。
let users = [ { FirstName = "Robert"; LastName = "Novak"; Occupation = "teacher"; Salary = 1770 } { FirstName = "John"; LastName = "Doe"; Occupation = "gardener"; Salary = 1230 } { FirstName = "Lucy"; LastName = "Novak"; Occupation = "accountant"; Salary = 670 } ]
我们有一个包含三个用户的列表。字段名与值之间用等号分隔。
λ dotnet fsi simple.fsx { FirstName = "Robert" LastName = "Novak" Occupation = "teacher" Salary = 1770 } { FirstName = "John" LastName = "Doe" Occupation = "gardener" Salary = 1230 } { FirstName = "Lucy" LastName = "Novak" Occupation = "accountant" Salary = 670 }
当我们将每个字段放在单独的一行时,可以省略分号。
type User = { FirstName: string LastName: string Occupation: string Salary: int } let users = [ { FirstName = "Robert" LastName = "Novak" Occupation = "teacher" Salary = 1770 } { FirstName = "John" LastName = "Doe" Occupation = "gardener" Salary = 1230 } { FirstName = "Lucy" LastName = "Novak" Occupation = "accountant" Salary = 670 } ] users |> List.iter (printfn "%A")
该程序在没有分号的情况下定义和创建记录。
F# 记录访问字段
记录的字段通过点字符进行访问。
type User = { Name: string; Occupation: string } let u = { Name = "John Doe" Occupation = "gardener" } printfn "%s" u.Name printfn "%s" u.Occupation
我们创建了一个带有两个字段的用户记录,然后打印出这些字段的值。字段名通过点字符进行访问。
λ dotnet fsi access.fsx John Doe gardener
F# 记录字段顺序
F# 通过字段的名称和类型来确定记录的类型,而不是字段使用的顺序。
type User = { Name: string; Occupation: string } let u1 = { Name = "John Doe" Occupation = "gardener" } let u2 = { Occupation = "driver" Name = "Roger Roe" } printfn "%A" u1 printfn "%A" u2
我们定义了两个记录对象。Name
和 Occupation
字段的定义顺序无关紧要。
λ dotnet fsi order.fsx { Name = "John Doe" Occupation = "gardener" } { Name = "Roger Roe" Occupation = "driver" }
F# 克隆记录
可以使用 with
从现有记录派生新记录。
type User = { Name: string; Occupation: string } let u1 = { Name = "John Doe" Occupation = "gardener" } printfn "%A" u1 let u2 = { u1 with Name = "Peter Smith"} printfn "%A" u2
在此示例中,我们基于现有用户克隆了一个新用户。
let u2 = { u1 with Name = "Peter Smith"}
我们从 user1 派生 user2;我们保留职业并更改姓名。
λ dotnet fsi clone.fsx { Name = "John Doe" Occupation = "gardener" } { Name = "Peter Smith" Occupation = "gardener" }
F# 记录输出
%A
说明符用于漂亮地打印元组、记录和联合类型。%O
用于其他对象,使用 ToString。
type User = { Name: string Occupation: string } override this.ToString() = sprintf "%s %s" this.Name this.Occupation let u1 = { Name = "John Doe" Occupation = "gardener" } let u2 = { Name = "Roger Roe" Occupation = "driver" } printfn "%A" u1 printfn "%O" u2
我们定义了一个记录类型,并重写了 ToString
方法。我们使用 %A
和 %O
说明符输出了记录。
λ dotnet fsi output.fsx { Name = "John Doe" Occupation = "gardener" } Roger Roe driver
F# 记录解构
解构是将类型解包成单个部分。
type User = { Name: string; Occupation: string } let u1 = { Name = "John Doe" Occupation = "gardener" } let { Name = n1; Occupation = o1 } = u1 printfn "%s %s" n1 o1 let { Name = _; Occupation = o2 } = u1 printfn "%s" o2 let { Name = n2 } = u1 printfn "%s" n2
该程序解构了一个用户记录。字段可以被省略。
λ dotnet fsi decons.fsx John Doe gardener gardener John Doe
F# 嵌套记录
我们可以使用 and
将一个记录嵌套在另一个记录中。
type User = { Name: string Occupation: string Address: Address } and Address = { Line1: string; Line2: string } let u1 = { Name = "John Doe" Occupation = "gardener" Address = { Line1 = "Address 1" Line2 = "Address 2" } } printfn "%A" u1 let u2 = { Name = "Roger Doe" Occupation = "driver" Address = { Line1 = "Address 1" Line2 = "Address 2" } } printfn "%A" u2
我们有一个 User
记录,其中嵌套了一个 Address
类型。
λ dotnet fsi nest.fsx { Name = "John Doe" Occupation = "gardener" Address = { Line1 = "Address 1" Line2 = "Address 2" } Colours = { Col1 = "red" Col2 = "blue" } } { Name = "Roger Doe" Occupation = "driver" Address = { Line1 = "Address 1" Line2 = "Address 2" } Colours = { Col1 = "red" Col2 = "green" } }
F# 记录相等性
记录具有结构相等性。结构相等性是指两个对象包含相同的值。
type User = { Name: string Occupation: string } let u1 = { Name = "John Doe" Occupation = "gardener" } let u2 = { Name = "Roger Roe" Occupation = "driver" } printfn "%A" (u1 = u2)
在此示例中,我们比较了两个用户记录。
F# 记录成员
记录中的成员可以使用 member
定义。
type User = { Name: string Occupation: string } member this.Info() = $"{this.Name} is a {this.Occupation}" let u1 = { Name= "John Doe"; Occupation="gardener" } let u2 = { Name= "Roger Roe"; Occupation="driver" } printfn "%s" (u1.Info()) printfn "%s" (u2.Info())
在此示例中,我们定义了 Info
成员。
λ dotnet fsi member.fsx John Doe is a gardener Roger Roe is a driver
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 | _ -> ()
该示例打印了所有姓 Doe 的人。
| { LastName = "Doe" } -> printfn "%A" user
在此分支中,我们检查所有 LastName
等于 "Doe" 的记录。
在本文中,我们使用 F# 中的记录类型进行了实践。