F#学习之路(6)列表类型

     列表在函数式编程中占据着重要的位置。在Lisp语言中,一切皆是列表,就连函数也是列表,列表在Lisp语言中发挥到了极致。F#语言列表语法来源于Ocaml,与Haskell语言也基本一致。本文只会讲解一些常用的使用方法,要很好的掌握列表,各位朋友可以google一下相关的内容。网络上对列表讨论比较深刻的大多以Haskell语言为例(Lisp语言列表很强大,但与大多数函数式语言列表的区别太大,不好借鉴)。

      一、如何定义列表

     在F#中定义一个列表,有几个方法。

     1、使用字量值

     


let l=[1;2;3]

let emptyList=List.empty

let lista=l @ emptyList

let listb=0::lista

     

     使用中括号'[',']',元素之间用分号分隔。不同于元组类型,元组使用'(',')',元素之间使用逗号分隔。

     F#中的列表是链表,列表元素必须是相同类型,列表是不可变的,因此定义后将不能改变。

     F#中定义一个空列表,有三种方法。它们在使用上有一些细微的差别。

     

printfn "%A" ((List.empty<int>).GetType()) 

printfn 
"%A" ((List.Empty:int list).GetType()) 

printfn 
"%A" (([]:int list).GetType())

     

     列表上定义了一个 @ 操作符,用于将一个列表追加到另一个列表的尾部,生成一个新的列表。而 :: 操作符,则将一个元素添加到一个列表的首部,形成一个新的列表。注意所有操作的列表本身不会改变。这种行为类似.net的string类型。

     

     2、使用范围表达式

 


let l1=[1..10]

let l2=[1..2..100]

let l3=['a'..'z']

     

     3、使用序列表达式

 

let l4=[for i in 1..10 do if  i>4 && i<8 then yield i]

     

     关于序列将在以后的博客中讨论,序列是一种计算表达式的实现。

 

     二、列表比较。

     

printfn "%b" ([1;2;3]>[1;2]) //true
printfn "%b" ([1;2]=[1;4]) //false

    

      列表类型可以很方便的进行值比较,要使用引用比较请使用obj.ReferenceEquals

     


printfn 
"%b" (obj.ReferenceEquals([],[])) //true
printfn "%b" (obj.ReferenceEquals([1],[1])) //false

 

     三、访问列表元素

     

printfn "%.2f" ([1.;2.].[1]) //2.00

 

     列表索引从0开始。

 

     四、使用列表类型属性     

     


printfn 
"%d" ([1..10].Length)  //10
printfn "%d" ([1;2;3].Head)   //1
printfn "%A" ([1;2;3].Tail)  //[2;3]
printfn "%A" ([1;2;3].IsEmpty) //false

 

     五、访问List模块操作列表 

 

 

Code

 

     上面的代码,使用namespace定义了一个FSharpLearning名称空间,使用type给int类型取了一个更有意义的名称LineNO。

     并且使用type定义了一个Order记录类型和Product记录类型,以及OrderStatus联合类型。

     之后使用module定义了一个子模块Order,并且这里使用[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]标注了Order模块。并告诉编译器在Order模块名后面加上Module作为模块名。前面说过顶层作用域不允许重名。

     Order模块中定义了几个函数,用来操作Order记录类型。之后,我们在ListTest模块中分别调用了这些定义的函数。

     List模块中函数较多,主要介绍三个,其它函数使用方法类似。

     


let numbers=[for i in 1..100 -> i]
numbers 
|>List.iteri (fun i num ->printfn "index %d:%d" i num)

      List.iteri函数类型为(int->'a->unit)->'a list->unit

      |>前向管道操作符。上面的字法等价于List.iteri (fun i num ->printfn "index %d:%d" i num) numbers

      List.iteri带有索引,还有一个List.iter不带索引,通常这两个函数用来遍历列表。

 

let number=[for i in 1..100 ->i]

numbers 
|>List.map (fun i->string i) |>printfn "%A"

     

     List.map的函数类型为('a->'b)->'a list->'b list

     map函数通常用来操作列表中每个元素,生成新的列表。 

 

let numbers=[for i in 1..100 ->i]

numbers 
|> List.reduce_left (+) |> printfn "%d"

 

     

     List.reduce_left函数类型为('a->'a->'a)->'a list ->'a

     reduce_left函数从左到右依次取出列表中两个元素操作。通常称为归约。

 

     六、列表与模式匹配

     

#light

let numbers=[for i in 1..100 -> i]

let rec print numbers=
    
match numbers with 
    
|[]->printfn "%s" ""
    
|[a]->printfn "%s" (string a)
    
|a::b ->printfn "%s" (string a);print b

print numbers

do System.Console.ReadKey(true)|>ignore

 

     总结:

     1、列表用于表示相同类型的一组数据的集合,在F#中大数据量集合类型应优先使用seq序列,因为序列是惰性的。

     2、列表是一个combinator,列表的类型定义为:

          type 'a list=

          |([])

          |( :: ) of 'a *'a list

     一个[1;2;3]的列表本质上是1::2::3::[],最具体的说是op_Cons(1,op_Cons(2,op_Cons(3,op_Nil)))

     op_Nil是[]的操作符名,op_Cons是::的操作符名。

     一个列表可以看作第一个元素(称为首部Head)与后面所有元素(称为尾部Tail)的组合。

     这样两个操作构成了列表的灵魂,深入研究组合子非常必要。

      下一篇:F#学习之路(7) 集合类型

 

标签: F#
posted @ 2008-09-06 00:18 lvxuwen 阅读(1321) 评论(8) 编辑 收藏

 回复 引用   
#1楼2008-09-06 10:02 | fftt[未注册用户]
呵呵
[for i in 1..10 do if i>4 && i<8 then yield i]
等于python中的
[x for x in range(1,10) if x>4 and x<8]

不过,F#支持这种吗?
[(x,y) for x in range(1,4) for y in [1,4]]
出来的结果是
[(1,1),(2,1),(3,1),(4,1),(1,4),(2,4),(3,5),(4,4)]

 回复 引用 查看   
#2楼2008-09-06 10:51 | Anders Cui      
@fftt
这个也是支持的,F#的列表推导特性

 回复 引用 查看   
#3楼[楼主]2008-09-06 10:59 | lvxuwen      
@fftt



我不懂具体的python语法



[for x in 1..4 do

for y in [1;4] do

yield x,y]



上面的语句是对应的c#3.0



for (var x = 1; x <=4; x++)

{

foreach (var y in new[] { 1, 4 })

{

yield return new { X = x, Y = y };

}

}



不过F#中的序列表达式中可以使用大多数控制结构。for,while,if 等等。理论上可以实现任何列表



let l=[for x in [1;4] do

for y in 1..4 do

yield y,x]

printfn "%A" l



结果为[(1,1),(2,1),(3,1),(4,1),(1,4),(2,4),(3,4),(4,4)]


 回复 引用 查看   
#4楼2008-09-06 11:42 | Anders Cui      
@lvxuwen
这样写更接近fftt的代码 :)
let tupleList =
[ for x in 1 .. 4
for y in 1 .. 4 -> x, y]

 回复 引用   
#5楼2008-09-06 13:48 | colder[未注册用户]
@ Anders Cui

最新的CTP版本中不造成在除seq之外的地方使用->了
建议的是:
[for x in 1 .. 4 do for y in 1 .. 4 do yield x, y]

 回复 引用 查看   
#6楼[楼主]2008-09-06 17:00 | lvxuwen      
@Anders Cui
@colder
是的,CTP版在语法做了简化、统一,除seq允许->外,其余的计算表达式都要使用标准的方法。
不过两位的写法,应该不是@fftt所要的结果吧。

 回复 引用 查看   
#7楼2008-09-06 20:04 | Anders Cui      
@colder
呵呵,还么看CTP的东东,落后了...

 回复 引用 查看   
#8楼[楼主]2008-09-07 16:15 | lvxuwen      
@Anders Cui

呵呵,昨天又增加了一个小版本1.9.6.2,看来F#小组动作还是很大的,除了不在考虑兼容1.x外,所有之前废弃的函数库都删除了,没有兼容的负担,相信到了正式版,F#会给我们一个清晰、一致的语言和函数库。