[置顶]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 < int int int int int int , Tuple < int int >>  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)列表类型 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值