F#是一种强类型的函数式编程语言,它提供了对列表(List)非常优秀的支持。列表是一系列有序且不可变的元素集合,这些元素必须具有相同的类型。本文将介绍如何在 F# 中创建和操作列表。
在 F# 中,有多种方式可以创建列表。以下是一些示例:
创建一个空列表:
let listEmpty = []
创建一个包含 1 到 10 的简单列表:
let list1 = [1 .. 10]
创建一个包含 1 到 10 之间奇数的简单列表,使用步长操作符:
let list2 = [1 .. 2 .. 10]
使用 for 循环创建列表:
let list3 = [for i in 1 .. 10 -> i * 2]
打印列表:
let prettyPrint desc list =
printfn desc
printfn "%A" list
调用 prettyPrint 函数打印列表:
prettyPrint "let listEmpty = []" listEmpty
prettyPrint "let list1 = [ 1 .. 10 ]" list1
prettyPrint "let list2 = [ 1 .. 2..10 ]" list2
prettyPrint "[ for i in 1 .. 10 -> i*2 ]" list3
执行上述代码后,将得到以下结果:
上一个示例展示了如何使用 for 循环创建列表,这是一种非常酷且强大的技术。在 F# 中,还有更酷更强大的工具,那就是“列表推导式”。列表推导式允许通过使用几乎任何标准的 F# 功能(包括函数/循环/条件等)来创建列表。
以下是使用列表推导式构建列表的示例:
let is2 x = match x with
| 2 -> "YES"
| _ -> "NO"
直接产生 1 到 3 的列表:
let list1 = [yield 1; yield 2; yield 3;]
产生 1 到 20 之间,使用 Math.Pow 函数返回新数字的列表:
let list2 = [for i in 1.0 .. 20.0 do yield Math.Pow(i, 2.0)]
只产生 1 到 20 之间可以被 5 整除的数字的列表:
let list3 = [for i in 1 .. 20 do if i % 5 = 0 then yield i]
根据源整数是否等于 2,产生 YES/NO 字符串的列表:
let list4 = [for i in 1 .. 5 -> is2 i]
执行上述代码后,将得到以下结果:
F#提供了多种操作符来处理列表,以下是一些示例:
使用 "::" 操作符可以将值追加到现有列表中。例如:
let list1 = [1; 2; 3; 4]
let list2 = 42 :: list1
执行上述代码后,将得到以下结果:
另一个有用的操作符是 "@",它允许连接具有相同类型的列表。例如:
let list1 = [1; 2; 3; 4]
let list2 = [5; 6; 7; 8]
let list3 = list1 @ list2
执行上述代码后,将得到以下结果:
F# 的 List 模块是一个非常重要的模块。MSDN 文档中对 List 模块的描述非常详细。以下是一些示例,但更多信息请参考 MSDN:
MSDN 页面地址:
列表具有一些有用的属性,如下表所示:
属性 | 类型 | 描述 |
---|---|---|
Head | 'T | 列表的第一个元素 |
Empty | 'T list | 返回适当类型的空列表的静态属性 |
IsEmpty | bool | 如果列表没有元素,则返回 true |
Item | 'T | 指定索引处的元素(从零开始) |
Length | int | 元素的数量 |
Tail | 'T list | 不包含第一个元素的列表 |
以下是属性的使用示例:
let list1 = [1; 2; 3]
// 属性
printfn "list1.IsEmpty is %b" (list1.IsEmpty)
printfn "list1.Length is %d" (list1.Length)
printfn "list1.Head is %d" (list1.Head)
printfn "list1.Tail.Head is %d" (list1.Tail.Head)
printfn "list1.Tail.Tail.Head is %d" (list1.Tail.Tail.Head)
printfn "list1.Item(1) is %d" (list1.Item(1))
执行上述代码后,将得到以下结果:
F#的 List 模块提供了大量函数,这里只介绍几个示例。更多信息请参考 MSDN:
MSDN 页面地址:
返回一个新的集合,其中只包含给定谓词返回 true 的元素。以下示例仅从列表中选择偶数以生成新列表:
let evenOnlyList = List.filter (fun x -> x % 2 = 0) [1; 2; 3; 4; 5; 6]
执行上述代码后,将得到以下结果:
返回给定函数返回 true 的第一个元素。以下示例中,1 到 100 的列表包含数字 5,5 是第一个可以被 5 整除的数字,因此是返回值:
let isDivisibleBy number elem = elem % number = 0
let result = List.find (isDivisibleBy 5) [1..100]
执行上述代码后,将得到以下结果:
测试集合的所有元素是否满足给定谓词。以下示例中,整个列表需要包含 0 才能返回 true:
let isAllZeroes list = List.forall (fun elem -> elem = 0.0) list
printfn "%b" (isAllZeroes [0.0; 0.0])
printfn "%b" (isAllZeroes [0.0; 1.0])
执行上述代码后,将得到以下结果:
将给定的函数应用于集合的每个元素。传递给函数的整数表示元素的索引:
let data = ["Cats"; "Dogs"; "Mice"; "Elephants"]
data |> List.iteri (fun i x -> printfn "item %d: %s" i x)
执行上述代码后,将得到以下结果:
使用给定的比较函数对给定的列表进行排序:
let list1 = [""; "&"; "&&"; "&&&"; "|"; "|||"; "|||"]
printfn "Before sorting: " list1 |> printfn "%A"
// 自定义排序函数
let sortFunction (string1: string) (string2: string) =
if string1.Length > string2.Length then 1 else
if string1.Length < string2.Length then -1 else
0
printfn "After sorting:\n%A" (List.sortWith sortFunction list1)
执行上述代码后,将得到以下结果:
在 F# 中,模式匹配列表也非常简单。例如:
let printIt desc x = printfn "%A %A" desc x
let patternMatchAList list =
match list with
| head :: tail ->
printIt "head=" head
printIt "tail=" tail
| [] -> ()
调用 patternMatchAList 函数:
patternMatchAList [1; 2; 3; 4; 5]
printfn "\r\n\r\n"
patternMatchAList [1; 2]
printfn "\r\n\r\n"
patternMatchAList [1]
执行上述代码后,将得到以下结果:
关于 F# 列表和模式匹配的讨论,如果不涉及递归,是不完整的。递归是一个将专门写一篇文章的主题,但目前让看看在 F# 中编写递归函数处理列表需要什么。以下是代码示例:
let printIt desc x = printfn "%A %A" desc x
let rec printList list =
match list with
| h :: t ->
printIt "head=" h
printIt "tail=" t
printList t
| [] -> ()
调用 printList 函数:
printList [1; 2; 3; 4; 5]