F#程序设计-函数式编程之Records

来源:互联网 发布:redis cluster java 编辑:程序博客网 时间:2024/05/18 03:39

当你想把数据组成一个结构化的格式,而不需要太复杂的语法时,你可以使用F#中的Record类型。Record类型与C语言的Struct类型基本一样,存储一组类型的值,通过字段的值来获取。定义一个Record类型很简单,只需要在大括号内定义系列的名称/类型就可以。要实例化一个Record,只需要提供对应的字段以及值即可,剩下的类型推断系统会根据你的输入来自动判断,比如:

> type PersonRec = {First : string; Last : string; Age : int};;

type PersonRec =
  {First: string;
   Last: string;
   Age: int;}

> let steve = {First = "Steve"; Last="Holt"; Age=21};;

val steve : PersonRec = {First = "Steve";
                         Last = "Holt";
                         Age = 21;}

> printfn "%s is %d years old" steve.First steve.Age;;
Steve is 21 years old
val it : unit = ()

 

Cloning Records

利用关键字"with"可以很容易的实现记录克隆,比如上面的那个steve有一个双胞胎哥哥,可以用下面的代码来实例化他哥哥,^_^:


> let bill = {steve with First="Bill"};;

val bill : PersonRec = {First = "Bill";
                        Last = "Holt";
                        Age = 21;}

如果克隆时需要更改多个字段的值,只需要用逗号分隔开来。

 

Pattern Matching

Record也可用于模式匹配,使用时只需要提供Record的字段值与给定的字面值都匹配即可,不如给定一辆车,将车的型号为品牌做匹配:

> type Car = {Model : string; Color : string};;

type Car =
  {Model: string;
   Color: string;}

> let matchCar newCars =
-     newCars
-     |> List.filter
-        (function
-             | {Model = "Coup" } -> true
-             | {Model = "BMW" } -> true
-             | _                -> false);;

val matchCar : Car list -> Car list

> let cars = [{Model = "Coup" ; Color = "Red"};{Model = "Benz" ; Color = "Black"
- }];;

val cars : Car list = [{Model = "Coup";
                        Color = "Red";}; {Model = "Benz";
                                          Color = "Black";}]

> matchCar cars;;
val it : Car list = [{Model = "Coup";
                      Color = "Red";}]

 

Type Inference

在F#中,类型推断系统是一个非常重要的东西,几乎所有的地方都需要用到它,现在来看看Record是在类型推断系统下工作的。在.NET中,类在使用前,必须标明它的类型,而在F#中,判断Record类型是根据Record的字段来的,就像我们上面的说,利用let绑定到一个大括号包围的数据,类型推断系统就能根据它的字段来推理出是Record。就拿下面的代码段来说,对于值pt1和pt2,没有任何地方标明它是什么类型,但是由于X和Y值被访问,并且F#编译器知道有一个Record包含有字段X和Y,自然就推断出pt1和pt2的类型了:

> type Point = {X : float; Y : float};;

type Point =
  {X: float;
   Y: float;}

> let distane pt1 pt2 =
-     let square x = x * x
-     sqrt <| square (pt1.X - pt2.X) + square (pt1.Y - pt2.Y);;

val distane : Point -> Point -> float

 

> distane { X = 0.0; Y = 0.0} {X = 10.0 ; Y = 10.0};;
val it : float = 14.14213562

 

当两个Record的具有相同的字段时,如果像上面这样写,那么类型推断系统就会发出一个错误了,因为它不知道你希望用到的是哪个类型。为了解决这个问题,你可以提供类型注释或者完整路径的字段,比如:

> type Point = {X : float; Y : float};;

type Point =
  {X: float;
   Y: float;}

> type Vector3 = {X : float; Y : float; Z : float};;

type Vector3 =
  {X: float;
   Y: float;
   Z: float;}

 

> let distance (pt1 : Point) (pt2 : Point) =
-     let square x = x * x
-     sqrt <| square (pt1.X - pt2.X) + square (pt1.Y - pt2.Y);;

val distance : Point -> Point -> float

 

引入字段的完整路径名可以像下面这样子:

> let origin = { Point.X = 0.0; Point.Y = 0.0 };;

val origin : Point = {X = 0.0;
                      Y = 0.0;}

 

 

Methods and Properties

在Record中也可以增加方法和属性:

> type Vector =
-     {X : float; Y : float; Z : float}
-     member this.Length =
-         sqrt <| this.X ** 2.0 + this.Y ** 2.0 + this.Z ** 2.0;;

type Vector =
  {X: float;
   Y: float;
   Z: float;}
  with
    member Length : float
  end

> let v = {X = 10.0; Y = 20.0; Z = 30.0};;

val v : Vector = {X = 10.0;
                  Y = 20.0;
                  Z = 30.0;}

> v.Length;;
val it : float = 37.41657387

 

原创粉丝点击