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.sumBya 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.maps 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 Somemož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.foldList.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.scans 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.

Viz také