Chapter3 - Defining Types - Record

F# 的类型系统提供了多种方法来让我们定义自己的类型。而所有的类型最后还是归于两种,

一种叫 Tuple 或者是 Records,他们跟 C 的 Struct 与 C# 的 Class 很相似。

另一种则是 sum 类型,有时他们也指 Union 类型。

 

下面我们先讲 Record

 

Tuple 是一种快速且简易的方式帮助我们将各个值封装到一个组里。而且也提供了方法让我们将 Tuple

里的值单独取出。看一个简单的例子;

 


#light

let pair = truefalse

let b1, _ = pair

let _, b2 = pair


 

在这里,我们使用下划线 _ 来表示忽略某个值,比如 b1, _ = pair 则表示我们只需要第一个值,此时则忽略了

第二个值,后面的表达式也是同一个意思。

 

而我们也可以使用关键字 type 来定义另一种类型。比如为一个 tuple 取别名,下例:

 


#light

type Name = string

type Fullname = string * string

let fullNameToString (x : Fullname) =
    
let first, second = x in
    first + 
" " + second


 

上例表示了传递一个 Fullname 类型,并将其转化为 string 的方法。而 Fullname 实际上就是一个 Tuple

 

Record 类型有点类似于 Tuple 可用于将多个类型的值绑定到一个别名或一个组,而他们的区别则主要

在于,Record 可以为绑定的值命名。下面看一个例子

 


#light

type Organization1 = { boss : string; lackeys : string list }

let rainbow =

    { boss = 
"Jeffrey";

      lackeys = [
"Zippy""George""Bungle"] }


type Organization2 = { chief : string ; underlings : string list }

type Organization3 = { chies : string ; indians : string list }

let thePlayers =

    { 
new Organization2

          
with chief = "Peter Quince"

          
and underlings = [ "Francis Flute""Robin Starveling";

                             
"Tom Snout""Snug""Nick Bottom"] }

let wayneManor =

    { 
new Organization3
         
with chief = "Batman"
         
and indians = ["Robin""Alfred"] }


 

F# 并没有强制 Record 里的命名必须是唯一的,所以当编译器无法推断出类型的时候,你最好还是加上类型声明。

 

而当如果你定义的类型里面需要使用到你下面即将定义的另外一个类型,

你可以使用 and 关键字来告诉编译器。看例子

 


#light

type recipe =
    { recipeName : 
string ;
      ingredients : ingreddient list;
      instructions : 
string }
and ingredient =
    { ingredientName : 
string ;
      quantity : 
int }

let greenBeansPineNuts =
    { recipeName = 
"Green Beans & Pine Nuts" ;

      ingredients =
          [ { ingredientName = 
"Green beans"; quantity = 250 };
            { ingredientName = 
"Pine nuts"; quantity = 250 }] //后面还有很多,略

      instructions = 
"Parboil the green beans for about minutes ........."}

 
let name = greenBeansPineNuts.recipeName

let toBuy =
    List.fold 
//原书为 fold_left 在 F# 2.0 中已取消
        (fun acc x ->
            acc +
            (Printf.sprintf 
"\t%s - %i\r\n" x.ingredientName x.quantity) )
        
"" greenBeansPineNuts.ingredients

let instructions = greenBeansPineNuts.instructions

printf 
"%s\r\n%s\r\n\r\n\t%s" name toBue instructions     


           

 

这里应该都没啥问题,唯一需要解释的可能是 fold函数。我们来看看他的定义

 

val it : (('a -> 'b -> 'a) -> 'a -> 'b list -> 'a) = <fun:clo@42>

参数的定义我就不解释了,免得越说越乱。他其实就是使用传递的第一个参数: 一个匿名函数

对第三个 参数 : 一个 b 类型的列表进行遍历,进行处理,然后第二个就是一个状态值,他用来保存

每次执行 匿名函数后的结果。

所以像上面的 toBuy 函数执行的结果其实就是将 ingredients 的每个元素按一定的规则转化为字符串,

并连接起来。

 

 

下面我们来看一个 Record 的模式匹配。

 

#light

type couple = { him : string; her : string }

let couples =

    [ { him = 
"Brad"; her = "Angelina" };
      { him = 
"Becks"; her = "Posh" };
      { him = 
"Chris"; her = "Gwyneth" };
      { him = 
"Michael" her = "Catherine" } ]

let rec findDivid l =
    
match l with
    
| { him = x ; her = "Posh" } :: tail -> x
    
| _ :: tail -> findDavid tail
    
| [] -> failwith "couldn't find David"

printf findDavid couples


 

上面的就比较简单易懂了,定义了一个 夫妻 (couple) 类型,里面有 丈夫 (him) 跟 妻子 (her) 两个字段,

然后定义了一个 夫妇 (couple)的列表, 接着的一个函数则在这个列表中查找第一个 妻子 (her) 名为

Posh 的夫妇,找到后把丈夫的名字赋予标识符 x ,然后返回 x, 如果没找到则抛出找不到的异常。

posted on 2010-09-27 09:00  兴说:  阅读(286)  评论(0编辑  收藏  举报