清單 (F#)
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 ]
您也可以使用迴圈建構來定義清單,如下列程式碼所示。
let listOfSquares = [ for i in 1 .. 10 -> i*i ]
空白清單是透過其間未含任何內容的一對方括號所指定。
// An empty list.
let listEmpty = []
您也可以使用序列運算式來建立清單。請參閱序列中的<序列運算式>。例如,下列程式碼會建立 1 到 10 之整數的平方清單。
let squaresList = [ 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 |
第一個項目。 |
|
'T list |
靜態屬性,它會傳回空白的適當型別清單。 |
|
bool |
如果清單沒有項目,則為 true。 |
|
'T |
在指定之索引處的項目 (以零為起始)。 |
|
int |
項目的數目。 |
|
'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 會使用預設泛型比較。泛型比較使用根據泛型比較函式的全域運算子來比較值。它可以與多種項目型別有效運作,例如簡單數字型別、Tuple、記錄、差別等位、清單、陣列和任何實作 IComparable 的型別。如果是實作 IComparable 的型別,則泛型比較會使用 CompareTo 函式。泛型比較也適用於字串,但是使用與文化特性無關的排列順序。泛型比較不應該用於不支援的型別 (例如函式型別)。而且,小型結構化型別的預設泛型比較效能最佳;如果是需要經常比較和排序的大型結構化型別,請考慮實作 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 會擲回 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。
清單和 Tuple
壓縮和解壓縮函式可以操作含有 Tuple 的清單。這些函式會將兩份具有單一值的清單合併為一份 Tuple 清單,或將一份 Tuple 清單分成兩個具有單一值的清單。最簡單的 List.zip 函式採用兩份具有單一項目的清單,然後產生一份 Tuple 雙重組的清單。另一個版本 (List.zip3) 採用三份具有單一項目的清單,然後產生一份具有三個項目之 Tuple 的清單。下列程式碼範例會示範 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) 採用多份 Tuple 清單,然後傳回 Tuple 中的清單,其中第一份清單包含每個 Tuple 的第一個項目,第二份清單則包含每個 Tuple 的第二個項目,以此類推。
下列程式碼範例會示範 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
摺疊和掃描作業
有些清單作業涉及所有清單項目之間的相互依存性。摺疊和掃描作業與 List.iter 和 List.map 類似,因為您對每個項目叫用函式,但是這些作業還提供另一個可以在計算階段存放資訊的參數,稱為「累計值」(Accumulator)。
請使用 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 acc elem -> 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。
其他作業
如需清單上其他作業的詳細資訊,請參閱程式庫參考主題 Collections.List 模組 (F#)。