Seznamy
Seznam v jazyce F# je uspořádaná neměnná řada prvků stejného typu. Pokud chcete provádět základní operace se seznamy, použijte funkce v modulu List.
Vytváření a inicializace seznamů
Seznam můžete definovat explicitním výpisem prvků oddělených středníky a uzavřenými v hranatých závorkách, jak je znázorněno v následujícím řádku kódu.
let list123 = [ 1; 2; 3 ]
Můžete také vložit konce řádků mezi prvky, v takovém případě středníky jsou volitelné. Druhá syntaxe může vést k čitelnějšímu kódu, pokud jsou výrazy inicializace prvků delší nebo pokud chcete zahrnout komentář pro každý prvek.
let list123 = [ 1; 2; 3 ]
Za normálních okolností musí být všechny prvky seznamu stejného typu. Výjimkou je, že seznam, ve kterém jsou prvky určeny jako základní typ, mohou obsahovat prvky, které jsou odvozené typy. Následující je tedy přijatelné, protože oba Button
a CheckBox
odvozené od Control
.
let myControlList: Control list = [ new Button(); new CheckBox() ]
Prvky seznamu můžete také definovat pomocí rozsahu označeného celými čísly oddělenými operátorem oblasti (..
), jak je znázorněno v následujícím kódu.
let list1 = [ 1..10 ]
Prázdný seznam je určen dvojicí hranatých závorek, mezi kterými není nic.
// An empty list.
let listEmpty = []
Seznam můžete vytvořit také pomocí sekvenčního výrazu. Další informace najdete v tématu Sekvenční výrazy . Například následující kód vytvoří seznam čtverců celých čísel od 1 do 10.
let listOfSquares = [ for i in 1..10 -> i * i ]
Operátory pro práci se seznamy
Prvky můžete k seznamu připojit pomocí operátoru ::
(cons). Pokud list1
je [2; 3; 4]
, následující kód vytvoří list2
jako [100; 2; 3; 4]
.
let list2 = 100 :: list1
Seznamy, které mají kompatibilní typy, můžete zřetězeny pomocí operátoru @
, jako v následujícím kódu. Pokud list1
je [2; 3; 4]
a list2
je [100; 2; 3; 4]
, tento kód vytvoří list3
jako [2; 3; 4; 100; 2; 3; 4]
.
let list3 = list1 @ list2
Funkce pro provádění operací se seznamy jsou k dispozici v modulu Seznam.
Vzhledem k tomu, že seznamy v jazyce F# jsou neměnné, všechny operace úprav generují nové seznamy místo úprav existujících seznamů.
Seznamy v jazyce F# se implementují jako ingly propojené seznamy, což znamená, že operace, které přistupují pouze k hlavní části seznamu, jsou O(1) a přístup k prvkům je O(n).
Vlastnosti
Typ seznamu podporuje následující vlastnosti:
Vlastnost | Type | Popis |
---|---|---|
Hlavy | 'T |
První prvek. |
Prázdné | 'T list |
Statická vlastnost, která vrátí prázdný seznam příslušného typu. |
IsEmpty | bool |
true pokud seznam neobsahuje žádné prvky. |
Položka | 'T |
Prvek v zadaném indexu (založený na nule). |
Délka | int |
Počet prvků. |
Ocas | 'T list |
Seznam bez prvního prvku. |
Následuje několik příkladů použití těchto vlastností.
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))
Použití seznamů
Programování se seznamy umožňuje provádět složité operace s malým množstvím kódu. Tato část popisuje běžné operace v seznamech, které jsou důležité pro funkční programování.
Rekurze se seznamy
Seznamy jsou jedinečně vhodné pro rekurzivní programovací techniky. Vezměte v úvahu operaci, která se musí provést u každého prvku seznamu. Můžete to udělat rekurzivně tak, že přejdete na hlavu seznamu a pak předáte konec seznamu, což je menší seznam, který se skládá z původního seznamu bez prvního prvku, zpět na další úroveň rekurze.
K napsání takové rekurzivní funkce použijete operátor cons (::
) v porovnávání vzorů, který umožňuje oddělit hlavu seznamu od konce.
Následující příklad kódu ukazuje, jak použít porovnávání vzorů k implementaci rekurzivní funkce, která provádí operace v seznamu.
let rec sum list =
match list with
| head :: tail -> head + sum tail
| [] -> 0
Předchozí kód funguje dobře pro malé seznamy, ale u větších seznamů by mohl zásobník přetéct. Následující kód tento kód vylepšuje pomocí argumentu akumulátoru, což je standardní technika pro práci s rekurzivními funkcemi. Použití argumentu akumulátoru činí funkci rekurzivní, což šetří prostor zásobníku.
let sum list =
let rec loop list acc =
match list with
| head :: tail -> loop tail (acc + head)
| [] -> acc
loop list 0
Funkce je rekurzivní funkce RemoveAllMultiples
, která přebírá dva seznamy. První seznam obsahuje čísla, jejichž násobky budou odebrány, a druhý seznam je seznam, ze kterého se mají čísla odebrat. Kód v následujícím příkladu používá tuto rekurzivní funkci k odstranění všech nevýkonných čísel ze seznamu a v důsledku toho ponechá seznam prvočísla.
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)
Výstup je následující:
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]
Funkce modulů
Modul List poskytuje funkce, které přistupuje k prvkům seznamu. Hlavní prvek je nejrychlejší a nejsnadnější přístup. Použijte vlastnost Head nebo funkci modulu List.head. K chvostu seznamu se dostanete pomocí vlastnosti Tail nebo funkce List.tail . Pokud chcete najít prvek podle indexu , použijte funkci List.nth . List.nth
prochází seznamem. Proto je to O(n). Pokud váš kód často používá List.nth
, můžete místo seznamu zvážit použití pole. Přístup k prvkům v polích je O(1).
Logické operace v seznamech
Funkce List.isEmpty určuje, zda seznam obsahuje nějaké prvky.
Funkce List.exists použije logický test na prvky seznamu a vrátí, true
pokud některý prvek splňuje test. List.exists2 je podobný, ale pracuje s po sobě jdoucími dvojicemi prvků ve dvou seznamech.
Následující kód ukazuje použití 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)
Výstup je následující:
For list [0; 1; 2; 3], contains zero is true
Následující příklad ukazuje použití 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
Výstup je následující:
Lists [1; 2; 3; 4; 5] and [5; 4; 3; 2; 1] have at least one equal element at the same position.
Seznam.forall můžete použít, pokud chcete otestovat, zda všechny prvky seznamu splňují podmínku.
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])
Výstup je následující:
true
false
Podobně List.forall2 určuje, zda všechny prvky v odpovídajících pozicích ve dvou seznamech splňují logický výraz, který zahrnuje každou dvojici prvků.
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])
Výstup je následující:
true
false
Operace řazení v seznamech
Seznamy řazení funkcí List.sort, List.sortBy a List.sortWith. Funkce řazení určuje, které z těchto tří funkcí se mají použít. List.sort
používá výchozí obecné porovnání. Obecné porovnání používá globální operátory založené na obecné porovnávací funkci k porovnání hodnot. Funguje efektivně s širokou škálou typů prvků, jako jsou jednoduché číselné typy, řazené kolekce členů, záznamy, diskriminované sjednocení, seznamy, pole a jakýkoli typ, který implementuje System.IComparable
. Pro typy, které implementují System.IComparable
, obecné porovnání používá System.IComparable.CompareTo()
funkci. Obecné porovnání funguje také s řetězci, ale používá pořadí řazení nezávislé na jazykové verzi. Obecné porovnání by se nemělo používat u nepodporovaných typů, jako jsou například typy funkcí. Výkon výchozího obecného porovnání je také nejvhodnější pro malé strukturované typy; u větších strukturovaných typů, které je potřeba často porovnávat a řadit, zvažte implementaci System.IComparable
a zajištění efektivní implementace System.IComparable.CompareTo()
metody.
List.sortBy
přebírá funkci, která vrací hodnotu, která se používá jako kritérium řazení, a List.sortWith
přebírá funkci porovnání jako argument. Tyto druhé dvě funkce jsou užitečné při práci s typy, které nepodporují porovnání, nebo pokud porovnání vyžaduje složitější sémantiku porovnání, jako v případě řetězců pracujících s jazykovou verzí.
Následující příklad ukazuje použití List.sort
.
let sortedList1 = List.sort [1; 4; 8; -2; 5]
printfn "%A" sortedList1
Výstup je následující:
[-2; 1; 4; 5; 8]
Následující příklad ukazuje použití List.sortBy
.
let sortedList2 = List.sortBy (fun elem -> abs elem) [1; 4; 8; -2; 5]
printfn "%A" sortedList2
Výstup je následující:
[1; -2; 4; 5; 8]
Další příklad ukazuje použití List.sortWith
. V tomto příkladu se funkce vlastního porovnání compareWidgets
používá k prvnímu porovnání jednoho pole vlastního typu a další, pokud jsou hodnoty prvního pole stejné.
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
Výstup je následující:
[{ID = 92;
Rev = 1;}; {ID = 92;
Rev = 1;}; {ID = 100;
Rev = 2;}; {ID = 100;
Rev = 5;}; {ID = 110;
Rev = 1;}]
Operace vyhledávání v seznamech
Seznamy podporují řadu operací hledání. Nejjednodušší list.find umožňuje najít první prvek, který odpovídá dané podmínce.
Následující příklad kódu ukazuje použití List.find
k nalezení prvního čísla, které je dělitelné 5 v seznamu.
let isDivisibleBy number elem = elem % number = 0
let result = List.find (isDivisibleBy 5) [ 1 .. 100 ]
printfn "%d " result
Výsledek je 5.
Pokud musí být prvky transformovány nejprve, volání List.pick, který přebírá funkci, která vrací možnost, a hledá první hodnotu možnosti, která je Some(x)
. Místo vrácení prvku List.pick
vrátí výsledek x
. Pokud nebyl nalezen žádný odpovídající prvek, List.pick
vyvolá System.Collections.Generic.KeyNotFoundException
. Následující kód ukazuje použití 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
Výstup je následující:
"b"
Jiná skupina vyhledávacích operací, List.tryFind a souvisejících funkcí, vrátí hodnotu možnosti. Funkce List.tryFind
vrátí první prvek seznamu, který splňuje podmínku, pokud takový prvek existuje, ale hodnota None
možnosti, pokud ne. Varianta List.tryFindIndex vrátí index elementu, pokud je nalezen, nikoli samotný prvek. Tyto funkce jsou znázorněny v následujícím kódu.
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."
Výstup je následující:
The first even value is 22.
The first even value is at position 8.
Aritmetické operace v seznamech
Běžné aritmetické operace, jako je součet a průměr, jsou integrované do modulu List. Chcete-li pracovat se seznam.sum, musí typ prvku seznamu podporovat +
operátor a mít nulovou hodnotu. Všechny předdefinované aritmetické typy splňují tyto podmínky. Pro práci se seznamem.average musí typ prvku podporovat dělení bez zbytku, který vylučuje celočíselné typy, ale umožňuje typy s plovoucí desetinou čárkou. Funkce List.sumBy a List.averageBy přebírají funkci jako parametr a výsledky této funkce slouží k výpočtu hodnot součtu nebo průměru.
Následující kód ukazuje použití List.sum
, List.sumBy
a 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
Výstup je 1.000000
.
Následující kód ukazuje použití List.averageBy
.
let avg2 = List.averageBy (fun elem -> float elem) [1 .. 10]
printfn "%f" avg2
Výstup je 5.5
.
Seznamy a řazené kolekce členů
Seznamy, které obsahují řazené kolekce členů, lze manipulovat pomocí funkcí ZIP a rozbalit je. Tyto funkce kombinují dva seznamy jednoduchých hodnot do jednoho seznamu řazených kolekcí členů nebo oddělují jeden seznam řazených kolekcí členů do dvou seznamů jednoduchých hodnot. Nejjednodušší List.zip funkce přebírá dva seznamy jednoduchých prvků a vytváří jeden seznam párů řazených kolekcí členů. Další verze List.zip3 přebírá tři seznamy jednoduchých prvků a vytvoří jeden seznam řazených kolekcí členů, které mají tři prvky. Následující příklad kódu ukazuje použití List.zip
.
let list1 = [ 1; 2; 3 ]
let list2 = [ -1; -2; -3 ]
let listZip = List.zip list1 list2
printfn "%A" listZip
Výstup je následující:
[(1, -1); (2, -2); (3; -3)]
Následující příklad kódu ukazuje použití List.zip3
.
let list3 = [ 0; 0; 0]
let listZip3 = List.zip3 list1 list2 list3
printfn "%A" listZip3
Výstup je následující:
[(1, -1, 0); (2, -2, 0); (3, -3, 0)]
Odpovídající rozbalené verze, List.unzip a List.unzip3, přebírají seznamy řazených kolekcí členů a vrátí seznamy v řazené kolekci členů, kde první seznam obsahuje všechny prvky, které byly první v každé řazené kolekci členů, a druhý seznam obsahuje druhý prvek každé řazené kolekce členů atd.
Následující příklad kódu ukazuje použití List.unzip.
let lists = List.unzip [(1,2); (3,4)]
printfn "%A" lists
printfn "%A %A" (fst lists) (snd lists)
Výstup je následující:
([1; 3], [2; 4])
[1; 3] [2; 4]
Následující příklad kódu ukazuje použití List.unzip3.
let listsUnzip3 = List.unzip3 [(1,2,3); (4,5,6)]
printfn "%A" listsUnzip3
Výstup je následující:
([1; 4], [2; 5], [3; 6])
Operace s elementy seznamu
Jazyk F# podporuje různé operace s prvky seznamu. Nejjednodušší je List.iter, který umožňuje volat funkci pro každý prvek seznamu. Varianty zahrnují List.iter2, který umožňuje provést operaci s prvky dvou seznamů List.iteri, což je podobné s List.iter
tím rozdílem, že index každého prvku je předán jako argument funkce, která je volána pro každý prvek, a List.iteri2, což je kombinace funkce List.iter2
a List.iteri
. Následující příklad kódu znázorňuje tyto funkce.
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
Výstup je následující:
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
Další často používanou funkcí, která transformuje prvky seznamu, je List.map, která umožňuje použít funkci pro každý prvek seznamu a vložit všechny výsledky do nového seznamu. List.map2 a List.map3 jsou varianty, které přebírají více seznamů. Kromě elementu můžete také použít List.mapi a List.mapi2, pokud kromě elementu musí být funkce předána index každého prvku. Jediným rozdílem mezi List.mapi2
seznamy a List.mapi
je, že List.mapi2
funguje se dvěma seznamy. Následující příklad znázorňuje List.map.
let list1 = [1; 2; 3]
let newList = List.map (fun x -> x + 1) list1
printfn "%A" newList
Výstup je následující:
[2; 3; 4]
Následující příklad ukazuje použití 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
Výstup je následující:
[5; 7; 9]
Následující příklad ukazuje použití List.map3
.
let newList2 = List.map3 (fun x y z -> x + y + z) list1 list2 [2; 3; 4]
printfn "%A" newList2
Výstup je následující:
[7; 10; 13]
Následující příklad ukazuje použití List.mapi
.
let newListAddIndex = List.mapi (fun i x -> x + i) list1
printfn "%A" newListAddIndex
Výstup je následující:
[1; 3; 5]
Následující příklad ukazuje použití List.mapi2
.
let listAddTimesIndex = List.mapi2 (fun i x y -> (x + y) * i) list1 list2
printfn "%A" listAddTimesIndex
Výstup je následující:
[0; 7; 18]
List.collect je podobný List.map
s tím rozdílem, že každý prvek vytvoří seznam a všechny tyto seznamy jsou zřetězeny do konečného seznamu. V následujícím kódu každý prvek seznamu generuje tři čísla. Všechny se shromažďují do jednoho seznamu.
let collectList = List.collect (fun x -> [for i in 1..3 -> x * i]) list1
printfn "%A" collectList
Výstup je následující:
[1; 2; 3; 2; 4; 6; 3; 6; 9]
Můžete také použít List.filter, který přebírá logickou podmínku a vytvoří nový seznam, který se skládá pouze z prvků, které splňují danou podmínku.
let evenOnlyList = List.filter (fun x -> x % 2 = 0) [1; 2; 3; 4; 5; 6]
Výsledný seznam je [2; 4; 6]
.
Kombinace mapování a filtru, List.choose umožňuje transformovat a vybírat prvky najednou. List.choose
použije funkci, která vrátí možnost pro každý prvek seznamu a vrátí nový seznam výsledků pro prvky, když funkce vrátí hodnotu Some
možnosti .
Následující kód ukazuje použití List.choose
výběru velkých písmen z seznamu slov.
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
Výstup je následující:
["Rome's"; "Bob's"]
Provoz na více seznamech
Seznamy se dají spojit dohromady. Pokud chcete spojit dva seznamy do jednoho, použijte List.append. Pokud chcete spojit více než dva seznamy, použijte 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
Operace skládání a skenování
Některé operace seznamu zahrnují vzájemné závislosti mezi všemi prvky seznamu. Operace skládání a skenování jsou podobné List.iter
a List.map
v tom, že vyvoláte funkci na každém prvku, ale tyto operace poskytují další parametr označovaný jako akumulátor , který přenáší informace prostřednictvím výpočtu.
Slouží List.fold
k provedení výpočtu v seznamu.
Následující příklad kódu ukazuje použití List.fold k provádění různých operací.
Seznam se prochází; akumulátor acc
je hodnota, která se předává při výpočtu. První argument přebírá akumulátor a prvek seznamu a vrátí dočasný výsledek výpočtu pro daný prvek seznamu. Druhým argumentem je počáteční hodnota akumulátoru.
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])
Verze těchto funkcí, které mají číslici v názvu funkce, pracují s více než jedním seznamem. Například List.fold2 provádí výpočty na dvou seznamech.
Následující příklad ukazuje použití 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
a List.scan se liší v tom, že List.fold
vrátí konečnou hodnotu extra parametru, ale List.scan
vrátí seznam mezilehlých hodnot (spolu s konečnou hodnotou) extra parametru.
Každá z těchto funkcí zahrnuje obrácenou variantu, například List.foldBack, která se liší v pořadí, v jakém se seznam prochází, a pořadí argumentů. List.fold
List.foldBack
A mají také varianty, List.fold2 a List.foldBack2, které mají dvě seznamy se stejnou délkou. Funkce, která se provádí na každém prvku, může k provedení určité akce použít odpovídající prvky obou seznamů. Typy prvků těchto dvou seznamů se mohou lišit, jako v následujícím příkladu, ve kterém jeden seznam obsahuje částky transakcí pro bankovní účet a druhý seznam obsahuje typ transakce: vklad nebo výběr.
// 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
Pro výpočet, jako je součet, a List.foldBack
mají stejný účinek, List.fold
protože výsledek nezávisí na pořadí procházení. V následujícím příkladu List.foldBack
se používá k přidání prvků v seznamu.
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])
Následující příklad se vrátí do příkladu bankovního účtu. Tentokrát se přidá nový typ transakce: výpočet úroku. Koncový zůstatek teď závisí na pořadí transakcí.
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
Funkce List.reduce je poněkud podobná List.fold
a List.scan
s tím rozdílem, že místo předávání kolem samostatného akumulátoru přebírá funkci, List.reduce
která přebírá dva argumenty typu prvku místo jednoho a jeden z těchto argumentů funguje jako akumulátor, což znamená, že ukládá průběžný výsledek výpočtu. List.reduce
začne provozem prvních dvou prvků seznamu a pak použije výsledek operace spolu s dalším prvkem. Vzhledem k tomu, že neexistuje samostatný akumulátor, který má vlastní typ, lze použít místo pouze tehdyList.fold
, List.reduce
když akumulátor a typ prvku mají stejný typ. Následující kód ukazuje použití List.reduce
. List.reduce
vyvolá výjimku, pokud zadaný seznam neobsahuje žádné prvky.
V následujícím kódu je prvním voláním výrazu lambda zadány argumenty 2 a 4 a vrátí 6 a další volání je dáno argumenty 6 a 10, takže výsledek je 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
Převod mezi seznamy a jinými typy kolekcí
Modul List
poskytuje funkce pro převod na sekvence a pole i z obou sekvencí a polí. Chcete-li převést na nebo z sekvence, použijte List.toSeq nebo List.ofSeq. Chcete-li převést na pole nebo z pole, použijte List.toArray nebo List.ofArray.
Další operace
Informace o dalších operacích se seznamy naleznete v referenčním tématu knihovny List Module.