列表
F# 中的列表是一个有序的、不可变的同类型元素系列。 若要对列表执行基本操作,请使用列表模块中的函数。
创建和初始化列表
可以通过以下方式来定义列表:显式列出元素并用分号分隔各个元素,然后将这些元素用方括号括起来,如下面的代码行所示。
let list123 = [ 1; 2; 3 ]
也可以在各个元素之间插入换行符,在这种情况下,分号是可选的。 当元素初始化表达式较长或你希望为每个元素包含一个注释时,后一种语法会产生可读性更高的代码。
let list123 = [ 1; 2; 3 ]
通常,所有列表元素都必须是同一类型。 但存在一种例外情况,即其元素被指定为基类型的列表中包含的元素可以是派生类型。 由于 Button
和 CheckBox
都派生自 Control
,因此以下内容是可接受的。
let myControlList: Control list = [ new Button(); new CheckBox() ]
也可以使用由范围分隔符 (..
) 分隔的整数所指示的范围来定义列表元素,如以下代码所示。
let list1 = [ 1..10 ]
可使用一对中间不包含任何内容的方括号来指定空列表。
// An empty list.
let listEmpty = []
也可以使用序列表达式来创建列表。 有关详细信息,请参阅序列表达式。 例如,以下代码创建一个从 1 到 10 的整数的平方值的列表。
let listOfSquares = [ for i in 1..10 -> i * i ]
用于处理列表的运算符
可以使用 ::
(cons) 运算符将元素附加到列表中。 如果 list1
为 [2; 3; 4]
,则以下代码会将 list2
创建为 [100; 2; 3; 4]
。
let list2 = 100 :: list1
可以使用 @
运算符来串联具有可兼容类型的列表,如以下代码所示。 如果 list1
为 [2; 3; 4]
,且 list2
为 [100; 2; 3; 4]
,则此代码会将 list3
创建为 [2; 3; 4; 100; 2; 3; 4]
。
let list3 = list1 @ list2
列表模块中提供了用于对列表执行操作的函数。
由于 F# 中的列表是不可变的,因此任何修改操作都会生成新列表,而不是修改现有列表。
F# 中的列表是作为单独链接的列表实现的,这意味着,仅访问列表头的操作的运算复杂度为 O(1),而元素访问的运算复杂度为 O(n)。
属性
列表类型支持以下属性:
属性 | 类型 | 说明 |
---|---|---|
头 | 'T |
第一个元素。 |
Empty | 'T list |
返回适合类型的空列表的静态属性。 |
IsEmpty | bool |
如果列表不包含任何元素,则为 true 。 |
项目 | 'T |
位于指定索引处(从零开始)的元素。 |
时长 | int |
元素数量。 |
Tail | 'T list |
不带第一个元素的列表。 |
以下是一些使用这些属性的示例。
let list1 = [ 1; 2; 3 ]
// Properties
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))
使用列表
利用列表进行编程,你可以使用少量代码来执行复杂的操作。 本节介绍针对列表的常见操作,这些操作对于函数编程很重要。
利用列表进行递归
列表特别适合于递归编程技术。 假设要对列表的每个元素执行某个操作。 可以通过以下方式来实现递归:对列表头执行操作,然后再次将列表尾(一个更小的列表,它由不带第一个元素的原始列表组成)传递回下一级递归。
若要编写此类递归函数,可在模式匹配中使用 cons 运算符 (::
),以便将列表头与列表尾分离。
以下代码示例显示了如何使用模式匹配来实现对列表执行操作的递归函数。
let rec sum list =
match list with
| head :: tail -> head + sum tail
| [] -> 0
上面的代码非常适用于小型列表,但对于大型列表,它可能会溢出堆栈。 以下代码通过使用累加器自变量(一种用于处理递归函数的标准技术)对该代码进行了改进。 使用累加器参数会使函数进行尾递归,这将节省堆栈空间。
let sum list =
let rec loop list acc =
match list with
| head :: tail -> loop tail (acc + head)
| [] -> acc
loop list 0
函数 RemoveAllMultiples
是一个递归函数,它采用两个列表。 第一个列表包含一些数字(将移除这些数字的倍数),第二个列表是要从中移除这些倍数数字的列表。 以下示例中的代码使用此递归函数来消除列表中的所有非质数,结果是得到一个质数列表。
let IsPrimeMultipleTest n x = x = n || x % n <> 0
let rec RemoveAllMultiples listn listx =
match listn with
| head :: tail -> RemoveAllMultiples tail (List.filter (IsPrimeMultipleTest head) listx)
| [] -> listx
let GetPrimesUpTo n =
let max = int (sqrt (float n))
RemoveAllMultiples [ 2..max ] [ 1..n ]
printfn "Primes Up To %d:\n %A" 100 (GetPrimesUpTo 100)
输出如下所示:
Primes Up To 100:
[2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 41; 43; 47; 53; 59; 61; 67; 71; 73; 79; 83; 89; 97]
模块函数
列表模块提供用于访问列表元素的函数。 访问头元素的速度最快且最为容易。 使用属性 Head 或模块函数 List.head。 可以使用Tail 属性或List.tail 函数访问列表尾。 若要按索引查找元素,请使用 List.nth 函数。 List.nth
将遍历列表。 因此,它的运算复杂度为 O(n)。 如果你的代码经常使用 List.nth
,那么可能需要考虑使用数组而不是列表。 数组中的元素访问的运算复杂度为 O(1)。
针对列表的布尔操作
List.isEmpty 函数可确定一个列表是否包含元素。
List.exists 函数对列表中的元素应用布尔测试,并在有元素满足测试条件时返回 true
。 List.exists2 函数与前者类似,只不过它是对两个列表中的连续元素对进行操作。
以下代码演示了 List.exists
的用法。
// Use List.exists to determine whether there is an element of a list satisfies a given Boolean expression.
// containsNumber returns true if any of the elements of the supplied list match
// the supplied number.
let containsNumber number list = List.exists (fun elem -> elem = number) list
let list0to3 = [0 .. 3]
printfn "For list %A, contains zero is %b" list0to3 (containsNumber 0 list0to3)
输出如下所示:
For list [0; 1; 2; 3], contains zero is true
以下示例演示了 List.exists2
的用法。
// Use List.exists2 to compare elements in two lists.
// isEqualElement returns true if any elements at the same position in two supplied
// lists match.
let isEqualElement list1 list2 = List.exists2 (fun elem1 elem2 -> elem1 = elem2) list1 list2
let list1to5 = [ 1 .. 5 ]
let list5to1 = [ 5 .. -1 .. 1 ]
if (isEqualElement list1to5 list5to1) then
printfn "Lists %A and %A have at least one equal element at the same position." list1to5 list5to1
else
printfn "Lists %A and %A do not have an equal element at the same position." list1to5 list5to1
输出如下所示:
Lists [1; 2; 3; 4; 5] and [5; 4; 3; 2; 1] have at least one equal element at the same position.
如果要测试列表的所有元素是否都满足某个条件,则可以使用 List.forall。
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])
输出如下所示:
true
false
同样地,List.forall2 可确定两个列表中相应位置处的所有元素是否满足涉及每对元素的布尔表达式的条件。
let listEqual list1 list2 = List.forall2 (fun elem1 elem2 -> elem1 = elem2) list1 list2
printfn "%b" (listEqual [0; 1; 2] [0; 1; 2])
printfn "%b" (listEqual [0; 0; 0] [0; 1; 0])
输出如下所示:
true
false
针对列表的排序操作
List.sort、List.sortBy 和 List.sortWith 函数可对列表进行排序。 排序函数可确定要使用上面三个函数中的哪一个函数。 List.sort
使用默认的泛型比较。 泛型比较根据泛型比较函数使用全局运算符来比较值。 它能够有效地处理各种元素类型,例如简单数值类型、元组、记录、可区分联合、列表、数组以及任何实现 System.IComparable
的类型。 对于实现 System.IComparable
的类型,泛型比较将使用 System.IComparable.CompareTo()
函数。 泛型比较还可处理字符串,只不过使用的是不依赖于区域性的排序顺序。 不应对不支持的类型(例如函数类型)使用泛型比较。 此外,默认泛型比较的性能最适用于小型结构化类型;对于需要经常比较和排序的大型结构化类型,请考虑实现 System.IComparable
并提供 System.IComparable.CompareTo()
方法的有效实现。
List.sortBy
使用一个函数,此函数返回一个用作排序条件的值,而 List.sortWith
将比较函数用作参数。 当你使用不支持比较的类型或比较需要更复杂的比较语义(对于区域性识别字符串)时,后面这两个函数会很有用。
以下示例演示了 List.sort
的用法。
let sortedList1 = List.sort [1; 4; 8; -2; 5]
printfn "%A" sortedList1
输出如下所示:
[-2; 1; 4; 5; 8]
以下示例演示了 List.sortBy
的用法。
let sortedList2 = List.sortBy (fun elem -> abs elem) [1; 4; 8; -2; 5]
printfn "%A" sortedList2
输出如下所示:
[1; -2; 4; 5; 8]
下一个示例演示了 List.sortWith
的用法。 在此示例中,使用自定义比较函数 compareWidgets
首先比较自定义类型的一个字段,在第一个字段的值相等的情况下,再比较另一个字段。
type Widget = { ID: int; Rev: int }
let compareWidgets widget1 widget2 =
if widget1.ID < widget2.ID then -1 else
if widget1.ID > widget2.ID then 1 else
if widget1.Rev < widget2.Rev then -1 else
if widget1.Rev > widget2.Rev then 1 else
0
let listToCompare = [
{ ID = 92; Rev = 1 }
{ ID = 110; Rev = 1 }
{ ID = 100; Rev = 5 }
{ ID = 100; Rev = 2 }
{ ID = 92; Rev = 1 }
]
let sortedWidgetList = List.sortWith compareWidgets listToCompare
printfn "%A" sortedWidgetList
输出如下所示:
[{ID = 92;
Rev = 1;}; {ID = 92;
Rev = 1;}; {ID = 100;
Rev = 2;}; {ID = 100;
Rev = 5;}; {ID = 110;
Rev = 1;}]
针对列表的搜索操作
可以对列表执行各种搜索操作。 最简单的搜索操作为 List.find,你可以通过此操作查找符合给定条件的第一个元素。
以下代码示例演示了如何使用 List.find
来查找列表中可被 5 整除的第一个元素。
let isDivisibleBy number elem = elem % number = 0
let result = List.find (isDivisibleBy 5) [ 1 .. 100 ]
printfn "%d " result
结果为 5。
如果必须先转换元素,则可调用 List.pick,此函数采用一个返回选项的函数,并查找第一个选项值 Some(x)
。 List.pick
返回结果 x
,而不是返回元素。 如果未找到匹配的元素,则 List.pick
将引发 System.Collections.Generic.KeyNotFoundException
。 以下代码显示了 List.pick
的用法。
let valuesList = [ ("a", 1); ("b", 2); ("c", 3) ]
let resultPick = List.pick (fun elem ->
match elem with
| (value, 2) -> Some value
| _ -> None) valuesList
printfn "%A" resultPick
输出如下所示:
"b"
另一组搜索操作(即 List.tryFind 和相关函数)返回一个选项值。 List.tryFind
函数返回列表中满足条件的第一个元素(如果该元素存在);如果该元素不存在,则返回选项值 None
。 变体 List.tryFindIndex 返回元素的索引(如果找到一个元素),而不是元素本身。 下面的代码中阐释了这些函数。
let list1d = [1; 3; 7; 9; 11; 13; 15; 19; 22; 29; 36]
let isEven x = x % 2 = 0
match List.tryFind isEven list1d with
| Some value -> printfn "The first even value is %d." value
| None -> printfn "There is no even value in the list."
match List.tryFindIndex isEven list1d with
| Some value -> printfn "The first even value is at position %d." value
| None -> printfn "There is no even value in the list."
输出如下所示:
The first even value is 22.
The first even value is at position 8.
针对列表的算术运算
列表模块中内置了常见的算术运算(例如求和与求平均值)。 若要使用 List.sum,列表元素类型必须支持 +
运算符并具有零值。 所有内置算术类型都满足这些条件。 若要使用 List.average,元素类型必须支持不带余数的除法,这将排除整型,但允许使用浮点型。 List.sumBy 和 List.averageBy 函数采用一个函数作为参数,并且此函数的结果用于计算求和或求平均值运算的值。
以下代码演示了 List.sum
、List.sumBy
和 List.average
的用法。
// Compute the sum of the first 10 integers by using List.sum.
let sum1 = List.sum [1 .. 10]
// Compute the sum of the squares of the elements of a list by using List.sumBy.
let sum2 = List.sumBy (fun elem -> elem*elem) [1 .. 10]
// Compute the average of the elements of a list by using List.average.
let avg1 = List.average [0.0; 1.0; 1.0; 2.0]
printfn "%f" avg1
输出为 1.000000
。
以下代码显示了 List.averageBy
的用法。
let avg2 = List.averageBy (fun elem -> float elem) [1 .. 10]
printfn "%f" avg2
输出为 5.5
。
列表和元组
包含元组的列表可由压缩和解压缩函数操作。 这些函数将两个包含单值的列表合并为一个元组列表,或将一个元组列表分成两个包含单值的列表。 最简单的 List.zip 函数采用两个包含单元素的列表,并生成一个包含元组对的列表。 另一个版本(即 List.zip3)采用三个包含单元素的列表,并生成一个包含三个元素的元组的列表。 以下代码示例演示了 List.zip
的用法。
let list1 = [ 1; 2; 3 ]
let list2 = [ -1; -2; -3 ]
let listZip = List.zip list1 list2
printfn "%A" listZip
输出如下所示:
[(1, -1); (2, -2); (3; -3)]
以下代码示例演示了 List.zip3
的用法。
let list3 = [ 0; 0; 0]
let listZip3 = List.zip3 list1 list2 list3
printfn "%A" listZip3
输出如下所示:
[(1, -1, 0); (2, -2, 0); (3, -3, 0)]
相应的解压缩版本(即 List.unzip 和 List.unzip3)采用元组列表并在一个元组中返回列表,其中第一个列表包含每个元组中的第一个元素,第二个列表包含每个元组中的第二个元素,依此类推。
以下代码示例演示了 List.unzip 的用法。
let lists = List.unzip [(1,2); (3,4)]
printfn "%A" lists
printfn "%A %A" (fst lists) (snd lists)
输出如下所示:
([1; 3], [2; 4])
[1; 3] [2; 4]
以下代码示例演示了 List.unzip3 的用法。
let listsUnzip3 = List.unzip3 [(1,2,3); (4,5,6)]
printfn "%A" listsUnzip3
输出如下所示:
([1; 4], [2; 5], [3; 6])
针对列表元素的操作
F# 支持针对列表元素执行各种操作。 最简单的操作为 List.iter,你可以使用此操作对列表中的每个元素调用一个函数。 此操作的变体包括 List.iter2(允许你对两个列表的元素执行操作)、List.iteri(与 List.iter
类似,不同之处在于它是将每个元素的索引作为一个自变量传递给对每个元素调用的函数)和 List.iteri2(结合了 List.iter2
和 List.iteri
的功能)。 以下代码示例阐释了这些函数。
let list1 = [1; 2; 3]
let list2 = [4; 5; 6]
List.iter (fun x -> printfn "List.iter: element is %d" x) list1
List.iteri(fun i x -> printfn "List.iteri: element %d is %d" i x) list1
List.iter2 (fun x y -> printfn "List.iter2: elements are %d %d" x y) list1 list2
List.iteri2 (fun i x y ->
printfn "List.iteri2: element %d of list1 is %d element %d of list2 is %d"
i x i y)
list1 list2
输出如下所示:
List.iter: element is 1
List.iter: element is 2
List.iter: element is 3
List.iteri: element 0 is 1
List.iteri: element 1 is 2
List.iteri: element 2 is 3
List.iter2: elements are 1 4
List.iter2: elements are 2 5
List.iter2: elements are 3 6
List.iteri2: element 0 of list1 is 1; element 0 of list2 is 4
List.iteri2: element 1 of list1 is 2; element 1 of list2 is 5
List.iteri2: element 2 of list1 is 3; element 2 of list2 is 6
另一个转换列表元素的常用函数为 List.map,你可以使用此函数对列表中的每个元素应用一个函数,并将所有结果放入一个新列表中。 List.map2 和 List.map3 是采用多个列表的变体。 如果除了需要向函数传递元素之外还需要传递每个元素的索引,则还可以使用 List.mapi 和 List.mapi2。 List.mapi2
和 List.mapi
之间的唯一区别在于 List.mapi2
使用了两个列表。 以下示例阐释了 List.map。
let list1 = [1; 2; 3]
let newList = List.map (fun x -> x + 1) list1
printfn "%A" newList
输出如下所示:
[2; 3; 4]
以下示例演示如何使用 List.map2
。
let list1 = [1; 2; 3]
let list2 = [4; 5; 6]
let sumList = List.map2 (fun x y -> x + y) list1 list2
printfn "%A" sumList
输出如下所示:
[5; 7; 9]
以下示例演示如何使用 List.map3
。
let newList2 = List.map3 (fun x y z -> x + y + z) list1 list2 [2; 3; 4]
printfn "%A" newList2
输出如下所示:
[7; 10; 13]
以下示例演示如何使用 List.mapi
。
let newListAddIndex = List.mapi (fun i x -> x + i) list1
printfn "%A" newListAddIndex
输出如下所示:
[1; 3; 5]
以下示例演示如何使用 List.mapi2
。
let listAddTimesIndex = List.mapi2 (fun i x y -> (x + y) * i) list1 list2
printfn "%A" listAddTimesIndex
输出如下所示:
[0; 7; 18]
List.collect 与 List.map
类似,只不过每个元素生成一个列表,并且所有这些列表将串联成一个最终列表。 在以下代码中,列表的每个元素均生成三个数字。 所有这些数字将收集到一个列表中。
let collectList = List.collect (fun x -> [for i in 1..3 -> x * i]) list1
printfn "%A" collectList
输出如下所示:
[1; 2; 3; 2; 4; 6; 3; 6; 9]
也可以使用 List.filter,它采用一个布尔值条件,并生成一个仅包含满足给定条件的元素的新列表。
let evenOnlyList = List.filter (fun x -> x % 2 = 0) [1; 2; 3; 4; 5; 6]
生成的列表为 [2; 4; 6]
。
List.choose 是映射和筛选器的组合,你可以使用它同时转换和选择元素。 List.choose
对列表的每个元素应用一个返回选项的函数,并在该函数返回选项值 Some
时为元素返回新的结果列表。
以下代码演示了如何使用 List.choose
从单词列表中选择大写单词。
let listWords = [ "and"; "Rome"; "Bob"; "apple"; "zebra" ]
let isCapitalized (string1:string) = System.Char.IsUpper string1[0]
let results = List.choose (fun elem ->
match elem with
| elem when isCapitalized elem -> Some(elem + "'s")
| _ -> None) listWords
printfn "%A" results
输出如下所示:
["Rome's"; "Bob's"]
针对多个列表的操作
可以将多个列表联接在一起。 若要将两个列表联接成一个列表,请使用 List.append。 若要联接两个以上的列表,请使用 List.concat。
let list1to10 = List.append [1; 2; 3] [4; 5; 6; 7; 8; 9; 10]
let listResult = List.concat [ [1; 2; 3]; [4; 5; 6]; [7; 8; 9] ]
List.iter (fun elem -> printf "%d " elem) list1to10
printfn ""
List.iter (fun elem -> printf "%d " elem) listResult
Fold 和 Scan 操作
一些列表操作涉及所有列表元素之间的相互依赖关系。 fold 和 scan 操作与 List.iter
和 List.map
类似,对每个元素调用一个函数,但这些操作提供了一个称作“累加器”的附加参数,用于在计算过程中传送信息。
使用 List.fold
可对列表执行计算。
以下代码示例演示了如何使用 List.fold 来执行各种操作。
将遍历列表;累加器 acc
是一个在计算过程中不断传递的值。 第一个参数采用累加器和列表元素,并返回针对列表元素的计算的中间结果。 第二个自变量为累加器的初始值。
let sumList list = List.fold (fun acc elem -> acc + elem) 0 list
printfn "Sum of the elements of list %A is %d." [ 1 .. 3 ] (sumList [ 1 .. 3 ])
// The following example computes the average of a list.
let averageList list = (List.fold (fun acc elem -> acc + float elem) 0.0 list / float list.Length)
// The following example computes the standard deviation of a list.
// The standard deviation is computed by taking the square root of the
// sum of the variances, which are the differences between each value
// and the average.
let stdDevList list =
let avg = averageList list
sqrt (List.fold (fun acc elem -> acc + (float elem - avg) ** 2.0 ) 0.0 list / float list.Length)
let testList listTest =
printfn "List %A average: %f stddev: %f" listTest (averageList listTest) (stdDevList listTest)
testList [1; 1; 1]
testList [1; 2; 1]
testList [1; 2; 3]
// List.fold is the same as to List.iter when the accumulator is not used.
let printList list = List.fold (fun acc elem -> printfn "%A" elem) () list
printList [0.0; 1.0; 2.5; 5.1 ]
// The following example uses List.fold to reverse a list.
// The accumulator starts out as the empty list, and the function uses the cons operator
// to add each successive element to the head of the accumulator list, resulting in a
// reversed form of the list.
let reverseList list = List.fold (fun acc elem -> elem::acc) [] list
printfn "%A" (reverseList [1 .. 10])
这些函数的各个版本(函数名中有一个数字)对多个列表执行操作。 例如,List.fold2 对两个列表执行计算。
以下示例演示了 List.fold2
的用法。
// Use List.fold2 to perform computations over two lists (of equal size) at the same time.
// Example: Sum the greater element at each list position.
let sumGreatest list1 list2 = List.fold2 (fun acc elem1 elem2 ->
acc + max elem1 elem2) 0 list1 list2
let sum = sumGreatest [1; 2; 3] [3; 2; 1]
printfn "The sum of the greater of each pair of elements in the two lists is %d." sum
List.fold
与 List.scan 的不同之处在于,List.fold
返回额外参数的最终值,而 List.scan
返回额外参数的中间值(连同最终值)的列表。
其中每个函数都包含一个反转变体(如 List.foldBack),该变体的不同之处在于列表的遍历顺序和自变量的顺序。 此外,List.fold
和 List.foldBack
也具有变体(即 List.fold2 和 List.foldBack2),它们采用两个长度相同的列表。 对每个元素执行的函数可以使用两个列表的对应元素来执行一些操作。 两个列表的元素类型可以不同(如以下示例所示),其中一个列表包含银行帐户的交易金额,而另一个列表包含交易的类型:存款或取款。
// Discriminated union type that encodes the transaction type.
type Transaction =
| Deposit
| Withdrawal
let transactionTypes = [Deposit; Deposit; Withdrawal]
let transactionAmounts = [100.00; 1000.00; 95.00 ]
let initialBalance = 200.00
// Use fold2 to perform a calculation on the list to update the account balance.
let endingBalance = List.fold2 (fun acc elem1 elem2 ->
match elem1 with
| Deposit -> acc + elem2
| Withdrawal -> acc - elem2)
initialBalance
transactionTypes
transactionAmounts
printfn "%f" endingBalance
对于类似于求和这样的计算,List.fold
和 List.foldBack
具有相同的效果,因为结果不依赖于遍历顺序。 在以下示例中,List.foldBack
用于在列表中添加元素。
let sumListBack list = List.foldBack (fun elem acc -> acc + elem) list 0
printfn "%d" (sumListBack [1; 2; 3])
// For a calculation in which the order of traversal is important, fold and foldBack have different
// results. For example, replacing fold with foldBack in the listReverse function
// produces a function that copies the list, rather than reversing it.
let copyList list = List.foldBack (fun elem acc -> elem::acc) list []
printfn "%A" (copyList [1 .. 10])
以下示例将返回到银行帐户示例。 这次,添加一个新的事务类型:利息计算。 期末余额现在取决于交易顺序。
type Transaction2 =
| Deposit
| Withdrawal
| Interest
let transactionTypes2 = [Deposit; Deposit; Withdrawal; Interest]
let transactionAmounts2 = [100.00; 1000.00; 95.00; 0.05 / 12.0 ]
let initialBalance2 = 200.00
// Because fold2 processes the lists by starting at the head element,
// the interest is calculated last, on the balance of 1205.00.
let endingBalance2 = List.fold2 (fun acc elem1 elem2 ->
match elem1 with
| Deposit -> acc + elem2
| Withdrawal -> acc - elem2
| Interest -> acc * (1.0 + elem2))
initialBalance2
transactionTypes2
transactionAmounts2
printfn "%f" endingBalance2
// Because foldBack2 processes the lists by starting at end of the list,
// the interest is calculated first, on the balance of only 200.00.
let endingBalance3 = List.foldBack2 (fun elem1 elem2 acc ->
match elem1 with
| Deposit -> acc + elem2
| Withdrawal -> acc - elem2
| Interest -> acc * (1.0 + elem2))
transactionTypes2
transactionAmounts2
initialBalance2
printfn "%f" endingBalance3
函数 List.reduce 有些类似于 List.fold
和 List.scan
,只不过 List.reduce
不是传递单独的累加器,而是采用一个函数,该函数采用元素类型的两个自变量(而不是仅采用一个自变量),其中的一个自变量将用作累加器,这意味着它将存储计算的中间结果。 List.reduce
首先对前两个列表元素执行操作,然后将操作的结果和下一个元素一起使用。 由于不存在具有自己的类型的单独累加器,因此只可以在累加器和元素类型的类型相同时,使用 List.reduce
代替 List.fold
。 以下代码演示了 List.reduce
的用法。 如果提供的列表中不包含任何元素,则 List.reduce
将引发异常。
在以下代码中,对 lambda 表达式的第一个调用提供了自变量 2 和 4,并返回 6;下一个调用提供了自变量 6 和 10,因此结果为 16。
let sumAList list =
try
List.reduce (fun acc elem -> acc + elem) list
with
| :? System.ArgumentException as exc -> 0
let resultSum = sumAList [2; 4; 10]
printfn "%d " resultSum
在列表和其他集合类型之间进行转换
List
模块提供了用于来回转换序列和数组的函数。 若要来回转换序列,请使用 List.toSeq 或 List.ofSeq。 若要来回转换数组,请使用 List.toArray 或 List.ofArray。
其他操作
有关针对列表的其他操作的信息,请参阅库参考主题列出模块。