Listy
Lista w języku F# jest uporządkowaną, niezmienną serią elementów tego samego typu. Aby wykonać podstawowe operacje na listach, użyj funkcji w module List.
Tworzenie i inicjowanie list
Listę można zdefiniować, jawnie wyświetlając elementy rozdzielone średnikami i ujęte w nawiasy kwadratowe, jak pokazano w poniższym wierszu kodu.
let list123 = [ 1; 2; 3 ]
Można również umieścić podziały wierszy między elementami, w tym przypadku średniki są opcjonalne. Ta ostatnia składnia może spowodować bardziej czytelny kod, gdy wyrażenia inicjowania elementu są dłuższe lub gdy chcesz dołączyć komentarz dla każdego elementu.
let list123 = [ 1; 2; 3 ]
Zwykle wszystkie elementy listy muszą być tego samego typu. Wyjątkiem jest to, że lista, w której elementy są określone jako typ podstawowy, może zawierać elementy, które są typami pochodnymi. W związku z tym następujące elementy są dopuszczalne, ponieważ zarówno , jak Button
i CheckBox
pochodzą z Control
klasy .
let myControlList: Control list = [ new Button(); new CheckBox() ]
Elementy listy można również zdefiniować przy użyciu zakresu wskazanego przez liczby całkowite oddzielone operatorem zakresu (..
), jak pokazano w poniższym kodzie.
let list1 = [ 1..10 ]
Pusta lista jest określana przez parę nawiasów kwadratowych z niczym między nimi.
// An empty list.
let listEmpty = []
Możesz również użyć wyrażenia sekwencji, aby utworzyć listę. Aby uzyskać więcej informacji, zobacz Wyrażenia sekwencji. Na przykład poniższy kod tworzy listę kwadratów liczb całkowitych z zakresu od 1 do 10.
let listOfSquares = [ for i in 1..10 -> i * i ]
Operatory do pracy z listami
Elementy można dołączać do listy przy użyciu ::
operatora (cons). Jeśli list1
parametr ma [2; 3; 4]
wartość , poniższy kod tworzy list2
jako [100; 2; 3; 4]
.
let list2 = 100 :: list1
Listy, które mają zgodne typy, można połączyć przy użyciu @
operatora , jak w poniższym kodzie. Jeśli list1
parametr to [2; 3; 4]
i list2
ma [100; 2; 3; 4]
wartość , ten kod tworzy list3
jako [2; 3; 4; 100; 2; 3; 4]
.
let list3 = list1 @ list2
Funkcje do wykonywania operacji na listach są dostępne w module Lista.
Ponieważ listy w języku F# są niezmienne, wszystkie operacje modyfikujące generują nowe listy zamiast modyfikować istniejące listy.
Listy w języku F# są implementowane jako listy połączone, co oznacza, że operacje, które uzyskują dostęp tylko do nagłówka listy, to O(1), a dostęp do elementów to O(n).
Właściwości
Typ listy obsługuje następujące właściwości:
Właściwość | Type | Opis |
---|---|---|
Głowy | 'T |
Pierwszy element. |
Pusty | 'T list |
Właściwość statyczna zwracająca pustą listę odpowiedniego typu. |
IsEmpty | bool |
true jeśli lista nie zawiera żadnych elementów. |
Produkt | 'T |
Element w określonym indeksie (oparty na zerze). |
Długość | int |
Liczba elementów. |
Ogon | 'T list |
Lista bez pierwszego elementu. |
Poniżej przedstawiono kilka przykładów użycia tych właściwości.
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))
Korzystanie z list
Programowanie z listami umożliwia wykonywanie złożonych operacji przy niewielkiej ilości kodu. W tej sekcji opisano typowe operacje na listach, które są ważne dla programowania funkcjonalnego.
Rekursja z listami
Listy są unikatowo dostosowane do technik programowania cyklicznego. Rozważ operację, która musi zostać wykonana na każdym elemenie listy. Można to zrobić rekursywnie, działając na czele listy, a następnie przekazując ogon listy, która jest mniejszą listą składającą się z oryginalnej listy bez pierwszego elementu, ponownie do następnego poziomu rekursji.
Aby napisać taką funkcję rekursywną, należy użyć operatora cons (::
) w dopasowywaniu wzorca, który umożliwia oddzielenie głowy listy od ogona.
W poniższym przykładzie kodu pokazano, jak używać dopasowania wzorca do implementowania funkcji cyklicznego wykonującej operacje na liście.
let rec sum list =
match list with
| head :: tail -> head + sum tail
| [] -> 0
Poprzedni kod działa dobrze w przypadku małych list, ale w przypadku większych list może przepełnić stos. Poniższy kod ulepsza ten kod przy użyciu argumentu akumulatorowego, standardowej techniki pracy z funkcjami rekursywnymi. Użycie argumentu akumulatorowego sprawia, że rekursywny ogon funkcji, co pozwala zaoszczędzić miejsce na stosie.
let sum list =
let rec loop list acc =
match list with
| head :: tail -> loop tail (acc + head)
| [] -> acc
loop list 0
Funkcja RemoveAllMultiples
jest funkcją rekursywną, która przyjmuje dwie listy. Pierwsza lista zawiera liczby, których wielokrotność zostanie usunięta, a druga lista to lista, z której mają zostać usunięte liczby. Kod w poniższym przykładzie używa tej funkcji rekursywnej, aby wyeliminować wszystkie liczby inne niż prime z listy, pozostawiając w wyniku listę liczb pierwszych.
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)
Wynik jest następujący:
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]
Funkcje modułu
Moduł Lista zawiera funkcje, które uzyskują dostęp do elementów listy. Element główny jest najszybszy i najłatwiejszy do uzyskania dostępu. Użyj właściwości Head lub funkcji modułu List.head. Dostęp do końca listy można uzyskać przy użyciu właściwości Tail lub funkcji List.tail . Aby znaleźć element według indeksu , użyj funkcji List.nth . List.nth
przechodzi przez listę. W związku z tym jest to O(n). Jeśli kod używa List.nth
często, warto rozważyć użycie tablicy zamiast listy. Dostęp do elementów w tablicach to O(1).
Operacje logiczne na listach
Funkcja List.isEmpty określa, czy lista ma jakiekolwiek elementy.
Funkcja List.exists stosuje test logiczny do elementów listy i zwraca true
, jeśli dowolny element spełnia test. List.exists2 jest podobny, ale działa na kolejnych parach elementów na dwóch listach.
Poniższy kod demonstruje użycie metody 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)
Wynik jest następujący:
For list [0; 1; 2; 3], contains zero is true
W poniższym przykładzie pokazano użycie metody 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
Wynik jest następujący:
Lists [1; 2; 3; 4; 5] and [5; 4; 3; 2; 1] have at least one equal element at the same position.
Jeśli chcesz sprawdzić, czy wszystkie elementy listy spełniają warunek, możesz użyć funkcji 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])
Wynik jest następujący:
true
false
Podobnie funkcja List.forall2 określa, czy wszystkie elementy w odpowiednich pozycjach na dwóch listach spełniają wyrażenie logiczne obejmujące każdą parę elementów.
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])
Wynik jest następujący:
true
false
Operacje sortowania na listach
Listy sortowania list.sort, List.sortBy i List.sortWith. Funkcja sortowania określa, które z tych trzech funkcji mają być używane. List.sort
używa domyślnego porównania ogólnego. Porównanie ogólne używa operatorów globalnych w oparciu o ogólną funkcję porównywania, aby porównać wartości. Działa wydajnie z wieloma różnymi typami elementów, takimi jak proste typy liczbowe, krotki, rekordy, związki dyskryminujące, listy, tablice i dowolny typ, który implementuje System.IComparable
element . W przypadku typów implementujących System.IComparable
porównanie ogólne używa System.IComparable.CompareTo()
funkcji . Porównanie ogólne działa również z ciągami, ale używa niezależnej od kultury kolejności sortowania. Porównanie ogólne nie powinno być używane w nieobsługiwanych typach, takich jak typy funkcji. Ponadto wydajność domyślnego porównania ogólnego jest najlepsza w przypadku małych typów strukturalnych; w przypadku większych typów strukturalnych, które muszą być porównywane i sortowane często, rozważ zaimplementowanie System.IComparable
i zapewnienie wydajnej implementacji System.IComparable.CompareTo()
metody.
List.sortBy
przyjmuje funkcję zwracającą wartość używaną jako kryterium sortowania i List.sortWith
przyjmuje funkcję porównania jako argument. Te dwie ostatnie funkcje są przydatne podczas pracy z typami, które nie obsługują porównania, lub gdy porównanie wymaga bardziej złożonych semantyki porównania, jak w przypadku ciągów obsługujących kulturę.
W poniższym przykładzie pokazano użycie metody List.sort
.
let sortedList1 = List.sort [1; 4; 8; -2; 5]
printfn "%A" sortedList1
Wynik jest następujący:
[-2; 1; 4; 5; 8]
W poniższym przykładzie pokazano użycie metody List.sortBy
.
let sortedList2 = List.sortBy (fun elem -> abs elem) [1; 4; 8; -2; 5]
printfn "%A" sortedList2
Wynik jest następujący:
[1; -2; 4; 5; 8]
W następnym przykładzie pokazano użycie metody List.sortWith
. W tym przykładzie funkcja porównania compareWidgets
niestandardowego służy do pierwszego porównywania jednego pola typu niestandardowego, a następnie drugiego, gdy wartości pierwszego pola są równe.
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
Wynik jest następujący:
[{ID = 92;
Rev = 1;}; {ID = 92;
Rev = 1;}; {ID = 100;
Rev = 2;}; {ID = 100;
Rev = 5;}; {ID = 110;
Rev = 1;}]
Operacje wyszukiwania na listach
Wiele operacji wyszukiwania jest obsługiwanych dla list. Najprostszy element List.find umożliwia znalezienie pierwszego elementu zgodnego z danym warunkiem.
W poniższym przykładzie kodu pokazano użycie metody List.find
w celu znalezienia pierwszej liczby, która jest podzielna przez 5 na liście.
let isDivisibleBy number elem = elem % number = 0
let result = List.find (isDivisibleBy 5) [ 1 .. 100 ]
printfn "%d " result
Wynik to 5.
Jeśli najpierw należy przekształcić elementy, wywołaj metodę List.pick, która przyjmuje funkcję zwracającą opcję i wyszukuje pierwszą wartość opcji o Some(x)
wartości . Zamiast zwracać element, List.pick
zwraca wynik x
. Jeśli nie zostanie znaleziony pasujący element, List.pick
zwraca wartość System.Collections.Generic.KeyNotFoundException
. Poniższy kod przedstawia użycie polecenia 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
Wynik jest następujący:
"b"
Kolejna grupa operacji wyszukiwania List.tryFind i powiązane funkcje zwracają wartość opcji. Funkcja List.tryFind
zwraca pierwszy element listy, który spełnia warunek, jeśli taki element istnieje, ale wartość None
opcji, jeśli nie. Właściwość list.tryFindIndex odmiany zwraca indeks elementu, jeśli zostanie znaleziony, a nie sam element. Te funkcje przedstawiono w poniższym kodzie.
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."
Wynik jest następujący:
The first even value is 22.
The first even value is at position 8.
Operacje arytmetyczne na listach
Typowe operacje arytmetyczne, takie jak suma i średnia, są wbudowane w moduł List. Aby pracować z list.sum, typ elementu listy musi obsługiwać +
operator i mieć wartość zero. Wszystkie wbudowane typy arytmetyczne spełniają te warunki. Aby pracować z list.average, typ elementu musi obsługiwać dzielenie bez reszty, która wyklucza typy całkowite, ale umożliwia używanie typów zmiennoprzecinkowych. Funkcje List.sumBy i List.averageBy przyjmują funkcję jako parametr, a wyniki tej funkcji są używane do obliczania wartości dla sumy lub średniej.
Poniższy kod demonstruje użycie elementów List.sum
, List.sumBy
i 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
Wynik to 1.000000
.
Poniższy kod przedstawia użycie polecenia List.averageBy
.
let avg2 = List.averageBy (fun elem -> float elem) [1 .. 10]
printfn "%f" avg2
Wynik to 5.5
.
Listy i krotki
Listy zawierające krotki można manipulować za pomocą funkcji zip i rozpakuj. Te funkcje łączą dwie listy pojedynczych wartości w jedną listę krotki lub rozdzielają jedną listę krotki na dwie listy pojedynczych wartości. Najprostsza funkcja List.zip przyjmuje dwie listy pojedynczych elementów i tworzy jedną listę par krotki. Inna wersja, List.zip3, przyjmuje trzy listy pojedynczych elementów i tworzy jedną listę krotki, które mają trzy elementy. W poniższym przykładzie kodu pokazano użycie metody List.zip
.
let list1 = [ 1; 2; 3 ]
let list2 = [ -1; -2; -3 ]
let listZip = List.zip list1 list2
printfn "%A" listZip
Wynik jest następujący:
[(1, -1); (2, -2); (3; -3)]
W poniższym przykładzie kodu pokazano użycie metody List.zip3
.
let list3 = [ 0; 0; 0]
let listZip3 = List.zip3 list1 list2 list3
printfn "%A" listZip3
Wynik jest następujący:
[(1, -1, 0); (2, -2, 0); (3, -3, 0)]
Odpowiednie wersje rozpakuj, List.unzip i List.unzip3, przyjmują listy krotki i zwracane listy w krotki, gdzie pierwsza lista zawiera wszystkie elementy, które były pierwsze w każdej krotki, a druga lista zawiera drugi element każdej krotki itd.
W poniższym przykładzie kodu pokazano użycie pliku List.unzip.
let lists = List.unzip [(1,2); (3,4)]
printfn "%A" lists
printfn "%A %A" (fst lists) (snd lists)
Wynik jest następujący:
([1; 3], [2; 4])
[1; 3] [2; 4]
W poniższym przykładzie kodu pokazano użycie pliku List.unzip3.
let listsUnzip3 = List.unzip3 [(1,2,3); (4,5,6)]
printfn "%A" listsUnzip3
Wynik jest następujący:
([1; 4], [2; 5], [3; 6])
Działanie na elementach listy
Język F# obsługuje różne operacje na elementach listy. Najprostszym elementem jest List.iter, który umożliwia wywołanie funkcji na każdym elemenie listy. Odmiany obejmują List.iter2, która umożliwia wykonywanie operacji na elementach dwóch list List.iteri, co jest podobne List.iter
do tego, że indeks każdego elementu jest przekazywany jako argument do funkcji wywoływanej dla każdego elementu i List.iteri2, która jest kombinacją funkcji List.iter2
i List.iteri
. Poniższy przykład kodu ilustruje te funkcje.
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
Wynik jest następujący:
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
Inną często używaną funkcją, która przekształca elementy listy, jest List.map, która umożliwia zastosowanie funkcji do każdego elementu listy i umieszczenie wszystkich wyników na nowej liście. List.map2 i List.map3 to odmiany, które przyjmują wiele list. Oprócz elementu można również użyć list.mapi i List.mapi2, jeśli oprócz elementu funkcja musi zostać przekazana indeksowi każdego elementu. Jedyną różnicą między elementami List.mapi2
i List.mapi
jest to, że List.mapi2
działa z dwiema listami. Poniższy przykład ilustruje list.map.
let list1 = [1; 2; 3]
let newList = List.map (fun x -> x + 1) list1
printfn "%A" newList
Wynik jest następujący:
[2; 3; 4]
W poniższym przykładzie pokazano użycie metody 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
Wynik jest następujący:
[5; 7; 9]
W poniższym przykładzie pokazano użycie metody List.map3
.
let newList2 = List.map3 (fun x y z -> x + y + z) list1 list2 [2; 3; 4]
printfn "%A" newList2
Wynik jest następujący:
[7; 10; 13]
W poniższym przykładzie pokazano użycie metody List.mapi
.
let newListAddIndex = List.mapi (fun i x -> x + i) list1
printfn "%A" newListAddIndex
Wynik jest następujący:
[1; 3; 5]
W poniższym przykładzie pokazano użycie metody List.mapi2
.
let listAddTimesIndex = List.mapi2 (fun i x y -> (x + y) * i) list1 list2
printfn "%A" listAddTimesIndex
Wynik jest następujący:
[0; 7; 18]
Funkcja List.collect jest podobna do List.map
, z tą różnicą, że każdy element tworzy listę, a wszystkie te listy są łączone w ostateczną listę. W poniższym kodzie każdy element listy generuje trzy liczby. Wszystkie te dane są zbierane na jednej liście.
let collectList = List.collect (fun x -> [for i in 1..3 -> x * i]) list1
printfn "%A" collectList
Wynik jest następujący:
[1; 2; 3; 2; 4; 6; 3; 6; 9]
Można również użyć atrybutu List.filter, który przyjmuje warunek logiczny i tworzy nową listę składającą się tylko z elementów spełniających dany warunek.
let evenOnlyList = List.filter (fun x -> x % 2 = 0) [1; 2; 3; 4; 5; 6]
Wynikowa lista to [2; 4; 6]
.
Kombinacja mapy i filtru List.select umożliwia przekształcanie i zaznaczanie elementów w tym samym czasie. List.choose
Stosuje funkcję zwracającą opcję do każdego elementu listy i zwraca nową listę wyników dla elementów, gdy funkcja zwraca wartość Some
opcji .
Poniższy kod demonstruje użycie metody List.choose
, aby wybrać wyrazy wielkich liter z listy wyrazów.
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
Wynik jest następujący:
["Rome's"; "Bob's"]
Obsługa na wielu listach
Listy można łączyć ze sobą. Aby dołączyć dwie listy do jednej, użyj funkcji List.append. Aby dołączyć więcej niż dwie listy, użyj polecenia 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
Operacje składania i skanowania
Niektóre operacje listy obejmują współzależności między wszystkimi elementami listy. Operacje składania i skanowania są podobne List.iter
List.map
do i w tym, że wywołujesz funkcję dla każdego elementu, ale te operacje zapewniają dodatkowy parametr nazywany akumulatorem , który przenosi informacje za pośrednictwem obliczeń.
Służy List.fold
do wykonywania obliczeń na liście.
W poniższym przykładzie kodu pokazano użycie funkcji List.fold do wykonywania różnych operacji.
Lista jest przechodzina; akumulator acc
jest wartością przekazywaną wraz z kontynuowaniem obliczeń. Pierwszy argument pobiera akumulator i element listy i zwraca wynik tymczasowy obliczenia dla tego elementu listy. Drugim argumentem jest początkowa wartość akumulowania.
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])
Wersje tych funkcji, które mają cyfrę w nazwie funkcji, działają na więcej niż jednej liście. Na przykład List.fold2 wykonuje obliczenia na dwóch listach.
W poniższym przykładzie pokazano użycie metody 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
i List.scan różnią się tym, że List.fold
zwraca ostateczną wartość dodatkowego parametru, ale List.scan
zwraca listę wartości pośrednich (wraz z końcową wartością) dodatkowego parametru.
Każda z tych funkcji zawiera odwrotną odmianę, na przykład List.foldBack, która różni się w kolejności przechodzenia listy i kolejności argumentów. List.fold
Ponadto i List.foldBack
mają odmiany List.fold2 i List.foldBack2, które mają dwie listy o równej długości. Funkcja wykonywana na każdym elemecie może używać odpowiednich elementów obu list, aby wykonać jakąś akcję. Typy elementów dwóch list mogą być różne, jak w poniższym przykładzie, w którym jedna lista zawiera kwoty transakcji dla konta bankowego, a druga lista zawiera typ transakcji: depozyt lub wypłata.
// 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
W przypadku obliczeń, takich jak sumowanie, i List.foldBack
mają taki sam efekt, List.fold
ponieważ wynik nie zależy od kolejności przechodzenia. W poniższym przykładzie List.foldBack
służy do dodawania elementów na liście.
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])
Poniższy przykład zwraca przykład konta bankowego. Tym razem dodawany jest nowy typ transakcji: obliczenie odsetek. Saldo końcowe zależy teraz od kolejności transakcji.
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
Funkcja List.reduce jest nieco podobna List.fold
i List.scan
, z tą różnicą, że zamiast przechodzić wokół oddzielnego akumulatora, przyjmuje funkcję, List.reduce
która przyjmuje dwa argumenty typu elementu zamiast tylko jednego, a jeden z tych argumentów działa jako akumulator, co oznacza, że przechowuje pośredni wynik obliczeń. List.reduce
uruchamia się przez działanie na pierwszych dwóch elementach listy, a następnie używa wyniku operacji wraz z następnym elementem. Ponieważ nie ma oddzielnego akumulatora, który ma własny typ, można użyć zamiast List.fold
tylko wtedy, List.reduce
gdy akumulator i typ elementu mają ten sam typ. Poniższy kod demonstruje użycie metody List.reduce
. List.reduce
zgłasza wyjątek, jeśli podana lista nie zawiera żadnych elementów.
W poniższym kodzie pierwsze wywołanie wyrażenia lambda otrzymuje argumenty 2 i 4, a następnie zwraca wartość 6, a następne wywołanie otrzymuje argumenty 6 i 10, więc wynik wynosi 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
Konwertowanie między listami i innymi typami kolekcji
Moduł List
udostępnia funkcje konwersji na i z obu sekwencji i tablic. Aby przekonwertować element na lub z sekwencji, użyj polecenia List.toSeq lub List.ofSeq. Aby przeprowadzić konwersję na tablicę lub z tablicy, użyj polecenia List.toArray lub List.ofArray.
Dodatkowe operacje
Aby uzyskać informacje o dodatkowych operacjach na listach, zobacz moduł listy tematów referencyjnych biblioteki.