7.1.1 使用 F# 记录类型

本文介绍了F#中的记录类型,包括其定义、使用方法及如何通过函数操作记录。记录是不可变的数据结构,可通过简洁的语法进行复制和修改。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

7.1.1 使用 F# 记录类型

 

    记录是带标记元组(labeled tuples)。它他们将多个不同的元素存储在一个值中;此外,每个元素都有一个可以用来访问它的名字。在 F# 中,元素的名字叫字段(fields)。这在很多方面类似于 C 的记录或结构构造,或者 C# 中的匿名类型。与匿名类型不同,记录必须事先声明。类似于匿名类型,记录,在其基本的形式中,包含唯一属性保存数据;清单 7.1 显示了表示矩形的这种声明。

 

Listing 7.1 Representing a rectangle using a record type (F# Interactive)

 

> type Rect =
     { Left : float32
       Top : float32
       Width : float32
       Height : float32 };;
type Rect = (...)

> let rc = { Left = 10.0f; Top = 10.0f;
Width = 100.0f; Height = 200.0f; };;
val rc : Rect = (...)

> rc.Left + rc.Width;;
val it : float32 = 110.0f

 

    当声明记录类型时,我们必须指定字段的类型和它的名字。在此例中,我们使用 float32 类型,它对应于 C# 中的 float 和 .NET 的 System.Single 类型,因为,在后面,我们需要这种类型的矩形。要创建 F# 记录的值,在大括号中指定所有字段的值。注意,我们不必写出记录类型名字,这是通过用字段的名字来自动推断的,你可以看到,在我们的示例中,编译器正确地推断出我们要创建矩形类型的值。与 C# 中匿名类型的工作相比,这是不同的。如果编译器基于字段的名字,找不到任何适当的记录类型,将报告错误。

    我们在处理记录时,需要读它们的字段,但还需要更改字段的值。例如,向右移动矩形。由于记录是一种函数式的数据结构,它是不可变的,我们将转而不得不用已修改的值创建一个记录。向右移动的矩形记录,写出来可能像这样:

 

let rc2 = { Left = rc.Left + 100.0f; Top = rc.Top;
                Width = rc.Width; Height = rc.Height }

 

    所有的代码都写成这样,会非常尴尬,因为,我们必须显式复制存储在记录中的所有字段值。此外,最终可能需要添加新字段来记录声明,这可能打乱现有的所有代码。F# 让我们以简洁的方式来表达这样的思想,"复制现有的记录,并作一些修改":

 

let rc2 = { rc with Left = rc.Left + 100.0f }

 

    使用 with 关键字,我们可以指定要更改的字段值,而所有其余字段会自动复制。这与前面的代码具有相同的含义,但它要实际得多。

    到目前为止,我们已经看到如何写记录的"原始"操作。当然,我们试图以函数风格写代码,所以,我们真正想能够用函数来操作记录。

 

处理记录

 

    我们将在本章后面使用 Rect 类型,需要两个简单的函数,来处理矩形。第一个函数是通过从每一条边上减去指定的宽度和高度,来缩小矩形,第二个是将我们的表示形式转换成 System.Drawing 命名空间中的 RectangleF 类。可以在清单 7.2 中看到两个函数。

 

Listing 7.2 Functions for working with rectangles (F# Interactive)

 

> open System.Drawing;;
> let deflate(original, wspace, hspace) =
     { Left = original.Left + wspace
        Top = original.Top + hspace
        Width = original.Width - (2.0f * wspace)
        Height = original.Height - (2.0f * hspace) };;
val deflate : Rect * float32 * float32 –> Rect

> let toRectangleF(original) =
     RectangleF(original.Left, original.Top,
                      original.Width, original.Height);;
val toRectangleF : Rect –> RectangleF

> { Left = 0.0f; Top = 0.0f;
      Width = 100.0f; Height = 100.0f; };;
val it : Rectangle = (...)

> deflate(it, 20.0f, 10.0f);;
val it : Rectangle = { Left = 20.0f; Top = 10.0f;
                              Width = 60.0f; Height = 80.0f;}

 

    从类型签名可以看到,F# 编译器正确推导出原始参数的类型是 Rect 类型,编译器使用在函数体中访问的字段名。如果我们有两个记录类型,并且仅使用由它们两个共享的字段,就必须显式指定其类型。我们可以使用类型注释,在函数声明中写上  (original:Rect)。通常,当使用 F# Interactive 时,可以立即测试函数。当创建值时,没我们有使用 let 绑定,所以,后面使用自动创建的值 it 来访问。

    总结一下,F# 的记录是不可变的,可以使用 {x with ...} 构造很容易地复制。如果我们在 C# 中,设计像这样的数据结构,要使用类,偶尔会用到结构,用特殊的方法写。在下一节中,我们将介绍如何做到这一点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值