列表在函数式编程中占据着重要的位置。在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 LineNo=int
type Order=
{ID:string;No:string;TotalPrice:float;Status:OrderStatus;OrderItems:List<LineNo*Product>}
and Product =
{ID:string;Name:string;Price:float}
and OrderStatus=
New
| Readed
| Confirmed
| Canceled
| Finished
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Order =
let change_order_status status order=
{order with Status=status}
let sum_order_totalprice order=
{order with TotalPrice=order.OrderItems |>List.sum_by (fun (i,product)->product.Price)}
let add_order_items productList order=
{order with OrderItems=productList |> List.mapi (fun i product ->(i,product))}
let reorder order =
{order with OrderItems=
order.OrderItems |> List.sort (fun (_,i) (_,j) ->compare i.Price j.Price)
|>List.mapi (fun i (_,product) ->(i,product)) }
module ListTest=
let main ()=
let productList=List.map3 (fun id name price->{ID=id;Name=name;Price=price})
["0001";"0002";"0003"] ["彩电";"冰箱";"PDA"] [4000.;3050.;3000.]
let order={ID="0001";No="20080904"; Status=New;TotalPrice=0.;OrderItems=[]}
let order=order |> Order.add_order_items productList
let order=Order.sum_order_totalprice order
let order=order |>Order.reorder
let order=Order.change_order_status Readed order
printfn "%A" order
do System.Console.ReadKey(true)|>ignore
do main()
上面的代码,使用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) 集合类型