Records是F#里用来表示简单数据的一种基本数据类型,当你想把数据组成一个结构化的格式,而不需要太复杂的语法时,你可以使用Record类型。

声明Records类型:

Records的语法格式如下:

    [ attributes ]

    type [accessibility-modifier] typename = {

        [ mutable ] label1 : type1;

        [ mutable ] label2 : type2;

        ...

        }

        member-list

这里我们定义一个简单的Point类型:

    type Point = { x : float; y: float; }

创建Records对象:

声明Point类型后,就可以定义Point对象了:

    let mypoint1 = { x = 1.0; y = 1.0; }

    let mypoint2 = { new Point with x = 1.0 and y = 1.0 }

这里我使用了两种方式创建了Point对象,mypoint1并没有指明类型,F#会自动根据其成员推导出其类型。但如果有两个不同的Records具有同样的成员,则会产生二义性,如下所示:

    type Point = { x : float; y: float; }

    type Point2D = { x: float; y: float; }

    let mypoint1 = { x = 1.0; y = 1.0; }

    let mypoint2 = { new Point with x = 1.0 and y = 1.0 }

这个时候,mypoint1就可能和两种类型Point和Point2D都对应,虽然这个时候有了二义性,但本身还是没有语法错误的,系统会自动将mypoint1推导为后定义的point2D类型。如果要定义Point类型,则只有使用mypoint2这种制定类型的方式创建。

另外,还可以通过表达式的方式创建Point对象:

    let mypoint3 = { mypoint1 with y=5.0 }

    //val mypoint3 : Point = {x = 1.0; y = 5.0;}

这里mypoint3继承了mypoint对象,并重新定义了y对象,非常直观,就不多讲了。

定义方法和属性:

定义方法和属性非常简单,示例如下:

    type Point = { x : float; y: float; }

       with

        member p.Length =

               sqrt <| p.x ** 2.0 + p.y ** 2.0

        member p.Show() =

               printf "%A" p

模式匹配

Records的模式匹配和Tuples类似,但由于其成员都有唯一的名称,匹配部分成员是可以不需要通配符,如下所示:

    let filterPoint (points:Point list) =

        points

        |> List.filter

           (function

            | {x = 1.0} | {y = 1.0} –> true

            | _ ->false)


filterPoint [mypoint1; mypoint2; mypoint3]

小结:

Records是F#的一种基本结构,用来定义简单的数据,使用起来也比较灵活而简单。

目前唯一不大习惯的是其创建方式,由于没有构造函数,成员不能使用缺省值,需要指定所有成员。这一点远没有C#创建对象并初始化成员那样来得灵活。并且由于其类型往往是靠推导出来的,创建的时候在VisualStudio中没有智能提示,一旦有一个成员名称拼写错误或类型不正确就有语法错误,这决定了其成员不宜过多,并且不宜使用过于复杂的名称,而C# 的IDE在这方面就要强大很多,基于其强大的智能提示,用C#写的代码基本上都是一次性成功,很少有语法错误。