Chapter3 - Defining Types - Record

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

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

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

 

下面我们先讲 Record

 

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

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

 


# light

let  pair =  true false

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, 如果没找到则抛出找不到的异常。

你可能感兴趣的:(type)