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#写的代码基本上都是一次性成功,很少有语法错误。