F#学习之路(5)元组类型

     元组类型,表示一组有序数据类型的集合。F#通过支持元组类型,方便了我们定义临时数据结构,而不需要为了临时的数据专门定义一个数据类型。

     一、元组的定义:     

 

 let tuple_2=(1,2

 
let tuple_3=("F#",1.9,"F# Function Language"

   

     在F#中元组使用小括号,元素之间逗号分隔来定义。元组元素可以是任何类型。

     上面代码中,tuple_2的类型是int*int,而tuple_3的类型为string*float*string。元组类型使用元组元素类型乘号连接的方式。

   

     元组类型可以作为函数的输入参数,也可以作为函数的返回值。    

 

let test (a,b,c) = 
    a+
1,b+2,c+3 

 

     上面函数的类型为 int*int*int->int*int*int

   

     元组类型虽然方便我们组织一组数据类型,而无需专门定义一个数据结构。但很显然他不能很好地帮助我们理解数据结构的语义,尤其是互操作时作为函数返回值的情况下。给元组类型取一个别名,能够部分缓解这个问题。

 

type FSharpDesc=string*float*string 

let getFSharpDesc ():FSharpDesc =
    (
"F#",1.9,"F# Function Language"

   

     二、元组类型的比较    

     在F#中元组类型可以进行比较,前提当然是同类型的元组类型。所谓同类型,是指元组的元素长度相等,元素类型相同。F#从前至后依次比较。    

 

do printfn "%b" ((1,2,3,4,5,6,7,8,9) <(2,1,2,3,4,5,6,7,8)) 

   

     打印的结果 为 true    

   

     三、元组与模式匹配 

      模式匹配在函数式语言中经常出现,其功能强大。比命令式语言中switch强大的多。不仅可以匹配基本类型、元组类型、列表类型、记录类型,还可以用于类型检测。在F#中还允许自定义类型的模式匹配,通过活动模式(Active Pattern)可以做到这一点。

     

let tuple_7=(1,2,3,4,5,6,(7,8,9)) 

let one,two,three,four,five,six,seven=tuple_7    

[one;two;three;four;five;six;] @ (
match seven with t1,t2,t3 ->[t1;t2;t3] ) 
|> List.iteri 
   (
fun index item ->printfn "%d,%A" index item) 

 

    元组类型不仅可以使用常用的模式匹配方法。 

   

 match seven with t1,t2,t3 ->[t1;t2;t3] 

    

     还可以使用隐式的模式匹配方法来分解元组

    

let one,two,three,four,five,six,seven=tuple_7

       

    这种隐式的模式匹配大大方便了我们使用元组类型的分解。上面的代码使用了列表,列表将在下一篇博客中讨论。

 

     四、元组类型的互操作

     在学习F#函数时,我们知道F#函数的输入参数是不需要括号的。

     

let f1 a b c =
  a+b+c

let f2 (a,b,c) =
  a+b+c

 

     上面的代码中f1和f2的类型是不一样的。

     f1的函数类型是int->int->int->int,而f2的函数类型是int*int*int->int。

     我把f1函数称为柯里化函数风格,而把f2函数称为元组函数风格。

     之所以称f1为柯里化的函数风格,是因为f1可以进行柯里化的函数调用,可以部分地传入参数值。而f2则必须全部传入。    

     两种函数风格的适用场景是什么了?

     柯里化的函数很显然比较适合扮演高阶函数。根据我目前所理解,在不讨论互操作的情况下,元组类型风格的函数总是可以通过柯里化的函数风格代替的。在不需要互操作的情况下,应该优先使用柯里化的函数风格,因为柯里化的函数可以更好的组合,从这个角度来说,柯里化的函数风格可以很好的组合代码,而元组类型的函数风格则用来组合数据。

     为什么说在互操作场景中优先使用元组类型的函数风格?

     回答上面的问题,则要讨论一下元组类型真正的类型是什么?什么叫元组类型真正的类型呀,元组类型不就是元组类型吗?呵呵,绕起来了。

     

let tuple_3=(1,2,3

 

     上面的tuple_3元组类型是int*int*int,这是对的。但这只是表面,实质上它的真实类型是Tuple<int,int,int>,F#编译器做了手脚。Tuple类型定义了一个泛型的记录类型,它有三个元素。

 

 type Tuple<'a,'b,'c> = 
    { Item1: 'a; Item2: 'b; Item3: 'c }

     

     在F#中,一共定义了六个泛型版的元组类型。从2个元素到七个元素的元组类型。很显然,元组类型最少两个元素,最多没有限制,理论上肯定是只要内存足够就可以了。F#如何处理超过七个元素的元组类型的了。根据我的测试,发现如果超过七个元素,超过的元素将变成一个嵌套的元组的成员,依此类推。举个例子:

 

let TestTupleWithGreaterThanSeven(a, b, c ) =
   (a,b,c,a+b,a+c,b+c,a+b+c,
10*a+2*b+c)

    

      上面的F#函数对应的c#方法签名是:

 

public static Tuple<intintintintintint, Tuple<intint>> TestTupleWithGreaterThanSeven(int a, int b, int c);

     

     注意上面的讨论是针对函数返回值的。对于函数输入参数并不适用,我们要分别讨论。

     对于函数来说,元组类型的函数风格与柯里化的函数风格,对于c#来说,并没有区别。或者说,柯时化的函数风格和元组类型的函数风格都会转化为c#方法风格。

 

let TestTuple(a, b, c,d,e,f,g,h ) =
    a+b+c+d+e+f+g+h

let TestCurrying a b c d e f g h =
    a+b+c+d+e+f+g+h

 

     对应的c#方法签名:

     

 public static int TestCurrying(int a, int b, int c, int d, int e, int f, int g, int h);
 
public static int TestTuple(int a, int b, int c, int d, int e, int f, int g, int h);

 

     一个有趣的例子:

 

let tuple_7=(1,2,3,4,5,6,(7,8,9))

let tuple_9=(1,2,3,4,5,6,7,8,9)

     上面的tuple_7和tuple_9在F#中是完全不同的类型,但对于c#来说,却是完全等效的。

 

     但当我们在类型上使用这两种风格的函数时就发生了变化,这种变化还很大。下面我就定义一个记录类型:

     

type Order = 

   {ID:string;Name:string;CreateDate:System.DateTime;Remark:string option}
    
with 
    
static member CreateWithCurrying id name createdate remark =
        {ID=id;Name=name;CreateDate=createdate;Remark=remark}
    
static member CreateWithTuple(id, name, createdate, remark) =
       {ID=id;Name=name;CreateDate=createdate;Remark=remark}

     

     我创建了一个记录类型,叫Order,并使用静态成员方法创建类型实例。

     c#签名如下:

     

 public static FastFunc<string, FastFunc<DateTime, FastFunc<Option<string>, TupleTest.Order>>> CreateWithCurrying(string id);
        
public static TupleTest.Order CreateWithTuple(string id, string name, DateTime createdate, Option<string> remark);
    

          

     这意味着什么,这意味着当我们在F#中使用柯里化风格的函数作为公开接口时,不是c#程序员习惯的调用方式。而采用元组风格的函数与c#程序员的习惯是一致的。

     在F#中元组的存在,使得接受多值的返回值时,F#比C#处理方式漂亮的多。 

 

match DateTime.TryParse("2008-8-25"with
| true, d -> printfn "%s" (d.ToString())
| _ -> printfn "datetime parse error"

         

 

     五、总结:

     (1) 元组类型一般用做临时数据的容器。业务数据结构应该选用记录类型或类、结构。

     (2) 当需要与其他.net语言互操作时,元组类型用做方法或函数的输入参数,有更好的兼容性,除非你的确需要柯里化的函数风格。在互操作时,元组类型不要作为函数的返回值,如果使用元组类型作为函数返回值,互操作的语言就必须引用F#的特定函数库。

     (3) 元组类型与模式匹配语法相结合,可以很好的组合、拆分。

       

     下一篇:F#学习之路(6)列表类型 

 

posted @ 2008-08-27 15:28  lvxuwen  阅读(2418)  评论(3编辑  收藏  举报