Elenchi
Un elenco in F# è una serie ordinata e non modificabile di elementi dello stesso tipo. Per eseguire operazioni di base sugli elenchi, usare le funzioni nel modulo Elenco.
Creazione e inizializzazione di elenchi
È possibile definire un elenco elencando in modo esplicito gli elementi, separati da punti e virgola e racchiusi tra parentesi quadre, come mostrato nella riga di codice seguente.
let list123 = [ 1; 2; 3 ]
È anche possibile inserire interruzioni di riga tra gli elementi e in questo caso i punti e virgola sono facoltativi. La sintassi precedente può agevolare la lettura del codice in caso di espressioni di inizializzazione degli elementi più lunghe o se si desidera includere un commento per ogni elemento.
let list123 = [ 1; 2; 3 ]
In genere, tutti gli elementi dell'elenco devono essere dello stesso tipo. Fanno eccezione gli elenchi in cui gli elementi vengono specificati come tipi di base. Gli elementi di tali elenchi possono essere tipi derivati. Sono pertanto accettabili gli elementi seguenti, poiché sia Button
che CheckBox
derivano da Control
.
let myControlList: Control list = [ new Button(); new CheckBox() ]
È anche possibile definire gli elementi dell'elenco usando un intervallo indicato da numeri interi separati dall'operatore di intervallo (..
), come mostrato nel codice seguente.
let list1 = [ 1..10 ]
Un elenco vuoto viene specificato da una coppia di parentesi quadre al cui interno non è presente alcun elemento.
// An empty list.
let listEmpty = []
È possibile anche usare un'espressione sequenza per creare un elenco. Per altre informazioni, vedere Espressioni di sequenza. Ad esempio, il codice seguente crea un elenco di quadrati di numeri interi compresi tra 1 e 10.
let listOfSquares = [ for i in 1..10 -> i * i ]
Operatori per l'uso di elenchi
È possibile associare elementi a un elenco usando l'operatore ::
(cons). Se list1
è [2; 3; 4]
, il codice seguente crea list2
come [100; 2; 3; 4]
.
let list2 = 100 :: list1
È possibile concatenare gli elenchi con tipi compatibili usando l'operatore @
, come nel codice seguente. Se list1
è [2; 3; 4]
e list2
è [100; 2; 3; 4]
, questo codice crea list3
come [2; 3; 4; 100; 2; 3; 4]
.
let list3 = list1 @ list2
Le funzioni per l'esecuzione di operazioni sugli elenchi sono disponibili nel modulo Elenco.
Poiché gli elenchi in F# non sono modificabili, qualsiasi operazione di modifica comporta la generazione di nuovi elenchi anziché la modifica di quelli esistenti.
Gli elenchi in F# vengono implementati come elenchi collegati singly, il che significa che le operazioni che accedono solo all'intestazione dell'elenco sono O(1) e l'accesso agli elementi è O(n).
Proprietà
Il tipo di elenco supporta le proprietà seguenti:
Proprietà | Type | Descrizione |
---|---|---|
Head | 'T |
Primo elemento. |
vuoto | 'T list |
Proprietà statica che restituisce un elenco vuoto del tipo appropriato. |
IsEmpty | bool |
true se nell'elenco non sono presenti elementi. |
Articolo | 'T |
Elemento in corrispondenza dell'indice specificato (a base zero). |
Lunghezza | int |
Numero di elementi. |
Tail | 'T list |
Elenco senza il primo elemento. |
Di seguito sono riportati alcuni esempi per l'uso di queste proprietà.
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))
Uso degli elenchi
L'uso degli elenchi nella programmazione consente di eseguire operazioni complesse con una piccola quantità di codice. Questa sezione descrive le operazioni comuni sugli elenchi, fondamentali ai fini della programmazione funzionale.
Ricorsione con elenchi
Gli elenchi sono particolarmente adatti per le tecniche di programmazione ricorsive. Nel caso di un'operazione da eseguire su ogni elemento di un elenco, è possibile operare in modo ricorsivo sull'elemento head dell'elenco e quindi passare all'elemento tail, ovvero un elenco di dimensioni ridotte costituito dall'elenco originale senza il primo elemento, per poi tornare al livello successivo di ricorsione.
Per scrivere una funzione ricorsiva di questo tipo, è necessario usare l'operatore cons (::
) nei criteri di ricerca, per separare l'elemento head dell'elenco dall'elemento tail.
L'esempio di codice seguente illustra come usare i criteri di ricerca per implementare una funzione ricorsiva che esegue le operazioni su un elenco.
let rec sum list =
match list with
| head :: tail -> head + sum tail
| [] -> 0
Il codice precedente è ideale per elenchi di piccole dimensioni, ma con gli elenchi di grandi dimensione potrebbe verificarsi un overflow dello stack. Il codice seguente migliora questo tipo di codice mediante un argomento dell'accumulatore, una tecnica standard per l'uso di funzioni ricorsive. L'uso dell'argomento accumulatore rende ricorsivo la funzione tail, risparmiando spazio nello stack.
let sum list =
let rec loop list acc =
match list with
| head :: tail -> loop tail (acc + head)
| [] -> acc
loop list 0
RemoveAllMultiples
è una funzione ricorsiva che accetta due elenchi. Il primo elenco contiene i numeri di cui verranno rimossi i multipli e il secondo è l'elenco da cui rimuovere i numeri. Il codice nell'esempio seguente usa questa funzione ricorsiva per eliminare tutti i numeri non primi da un elenco, restituendo come risultato un elenco di numeri primi.
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)
L'output è il seguente:
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]
Funzioni di modulo
Il modulo List fornisce funzioni che accedono agli elementi di un elenco. L'elemento head è il più veloce e più semplice a cui accedere. Usare la proprietà Head o la funzione del modulo List.head. È possibile accedere alla parte finale di un elenco usando la proprietà Tail o la funzione List.tail . Per trovare un elemento per indice, usare la funzione List.nth . List.nth
attraversa l'elenco Pertanto, è O(n). Se il codice usa List.nth
in modo frequente, è opportuno considerare l'uso di una matrice anziché di un elenco L'accesso agli elementi nelle matrici è O(1).
Operazioni booleane sugli elenchi
La funzione List.isEmpty determina se un elenco contiene elementi.
La funzione List.exists applica un test booleano agli elementi di un elenco e restituisce true
se un elemento soddisfa il test. List.exists2 è simile ma opera su coppie successive di elementi in due elenchi.
Il codice seguente illustra l'uso di 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)
L'output è il seguente:
For list [0; 1; 2; 3], contains zero is true
L'esempio seguente illustra l'uso di 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
L'output è il seguente:
Lists [1; 2; 3; 4; 5] and [5; 4; 3; 2; 1] have at least one equal element at the same position.
È possibile usare List.forall se si desidera verificare se tutti gli elementi di un elenco soddisfano una condizione.
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])
L'output è il seguente:
true
false
Analogamente, List.forall2 determina se tutti gli elementi nelle posizioni corrispondenti in due elenchi soddisfano un'espressione booleana che coinvolge ogni coppia di elementi.
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])
L'output è il seguente:
true
false
Operazioni di ordinamento sugli elenchi
Elenchi di ordinamento delle funzioni List.sort, List.sortBy e List.sortWith . La funzione di ordinamento determina quali funzioni usare tra le tre indicate in precedenza. List.sort
usa il confronto generico predefinito. Il confronto generico usa gli operatori globali basati sulla funzione di confronto generico per confrontare i valori. È perfettamente compatibile con un'ampia gamma di tipi di elemento, ad esempio tipi numerici semplici, tuple, record, unioni discriminate, elenchi, matrici e qualsiasi tipo che implementa System.IComparable
. Per i tipi che implementano System.IComparable
, il confronto generico usa la funzione System.IComparable.CompareTo()
. Il confronto generico usa anche le stringhe, ma adotta un ordinamento indipendente dalle impostazioni cultura. Non è opportuno usare il confronto generico sui tipi non supportati, ad esempio i tipi di funzione. Le prestazioni del confronto generico predefinito risultano inoltre ottimali per i tipi strutturati di piccole dimensioni, mentre per i tipi strutturati di grandi dimensioni, che devono essere confrontati e ordinati con frequenza, provare a implementare System.IComparable
e a fornire un'implementazione efficiente del metodo System.IComparable.CompareTo()
.
List.sortBy
accetta una funzione che restituisce un valore usato come criterio di ordinamento e List.sortWith
accetta una funzione di confronto come argomento. Queste ultime due funzioni risultano utili nel caso in cui vengano usati tipi che non supportano il confronto oppure se il confronto richiede una semantica più complessa, come nel caso di stringhe dipendenti dalle impostazioni cultura.
L'esempio seguente illustra l'uso di List.sort
.
let sortedList1 = List.sort [1; 4; 8; -2; 5]
printfn "%A" sortedList1
L'output è il seguente:
[-2; 1; 4; 5; 8]
L'esempio seguente illustra l'uso di List.sortBy
.
let sortedList2 = List.sortBy (fun elem -> abs elem) [1; 4; 8; -2; 5]
printfn "%A" sortedList2
L'output è il seguente:
[1; -2; 4; 5; 8]
L'esempio seguente illustra l'uso di List.sortWith
. In questo esempio, la funzione di confronto personalizzato compareWidgets
viene usata per confrontare in primo luogo un campo di un tipo personalizzato, quindi un altro campo, se i valori del primo campo sono uguali.
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
L'output è il seguente:
[{ID = 92;
Rev = 1;}; {ID = 92;
Rev = 1;}; {ID = 100;
Rev = 2;}; {ID = 100;
Rev = 5;}; {ID = 110;
Rev = 1;}]
Operazioni di ricerca sugli elenchi
Per gli elenchi sono supportate numerose operazioni di ricerca. Il più semplice, List.find, consente di trovare il primo elemento che corrisponde a una determinata condizione.
L'esempio di codice seguente illustra l'uso di List.find
per trovare il primo numero divisibile per 5 in un elenco.
let isDivisibleBy number elem = elem % number = 0
let result = List.find (isDivisibleBy 5) [ 1 .. 100 ]
printfn "%d " result
Il risultato è 5.
Se gli elementi devono essere trasformati per primi, chiamare List.pick, che accetta una funzione che restituisce un'opzione e cerca il primo valore dell'opzione che è Some(x)
. Anziché restituire l'elemento, List.pick
restituisce il risultato x
. Se non viene trovato alcun elemento corrispondente, List.pick
genera un'eccezione System.Collections.Generic.KeyNotFoundException
. Il codice seguente illustra l'uso di 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
L'output è il seguente:
"b"
Un altro gruppo di operazioni di ricerca, List.tryFind e funzioni correlate, restituisce un valore di opzione. La funzione List.tryFind
restituisce il primo elemento di un elenco che soddisfa una condizione se tale elemento è presente e il valore di opzione None
in caso contrario. La variante List.tryFindIndex restituisce l'indice dell'elemento, se presente, anziché l'elemento stesso. Queste funzioni vengono illustrate nel codice seguente.
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."
L'output è il seguente:
The first even value is 22.
The first even value is at position 8.
Operazioni aritmetiche sugli elenchi
Le operazioni aritmetiche comuni, ad esempio somma e media, sono incorporate nel modulo Elenco. Per usare List.sum, il tipo di elemento list deve supportare l'operatore +
e avere un valore zero. Tutti i tipi aritmetici incorporati soddisfano queste condizioni. Per lavorare con List.average, il tipo di elemento deve supportare la divisione senza un resto, che esclude i tipi integrali, ma consente tipi a virgola mobile. Le funzioni List.sumBy e List.averageBy accettano una funzione come parametro e i risultati di questa funzione vengono usati per calcolare i valori per la somma o la media.
Il codice seguente illustra l'uso di List.sum
, List.sumBy
e 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
L'output è 1.000000
.
Il codice seguente illustra l'uso di List.averageBy
.
let avg2 = List.averageBy (fun elem -> float elem) [1 .. 10]
printfn "%f" avg2
L'output è 5.5
.
Elenchi e tuple
Gli elenchi che contengono tuple possono essere modificati da funzioni di compressione e decompressione. Queste funzioni combinano due elenchi di valori singoli in un elenco di tuple o separano un elenco di tuple in due elenchi di valori singoli. La funzione List.zip più semplice accetta due elenchi di singoli elementi e produce un singolo elenco di coppie di tuple. Un'altra versione, List.zip3, accetta tre elenchi di singoli elementi e produce un singolo elenco di tuple con tre elementi. L'esempio di codice seguente illustra l'uso di List.zip
.
let list1 = [ 1; 2; 3 ]
let list2 = [ -1; -2; -3 ]
let listZip = List.zip list1 list2
printfn "%A" listZip
L'output è il seguente:
[(1, -1); (2, -2); (3; -3)]
L'esempio di codice seguente illustra l'uso di List.zip3
.
let list3 = [ 0; 0; 0]
let listZip3 = List.zip3 list1 list2 list3
printfn "%A" listZip3
L'output è il seguente:
[(1, -1, 0); (2, -2, 0); (3, -3, 0)]
Le versioni di decompressione corrispondenti, List.unzip e List.unzip3, accettano elenchi di tuple e restituiscono elenchi in una tupla, in cui il primo elenco contiene tutti gli elementi che sono stati prima in ogni tupla e il secondo elenco contiene il secondo elemento di ogni tupla e così via.
Nell'esempio di codice seguente viene illustrato l'uso di List.unzip.
let lists = List.unzip [(1,2); (3,4)]
printfn "%A" lists
printfn "%A %A" (fst lists) (snd lists)
L'output è il seguente:
([1; 3], [2; 4])
[1; 3] [2; 4]
Nell'esempio di codice seguente viene illustrato l'uso di List.unzip3.
let listsUnzip3 = List.unzip3 [(1,2,3); (4,5,6)]
printfn "%A" listsUnzip3
L'output è il seguente:
([1; 4], [2; 5], [3; 6])
Operazioni sugli elementi dell'elenco
F# supporta un'ampia gamma di operazioni sugli elementi di un elenco. Il più semplice è List.iter, che consente di chiamare una funzione su ogni elemento di un elenco. Le varianti includono List.iter2, che consente di eseguire un'operazione sugli elementi di due elenchi, List.iteri, che è simile List.iter
al fatto che l'indice di ogni elemento viene passato come argomento alla funzione chiamata per ogni elemento e List.iteri2, che è una combinazione della funzionalità di List.iter2
e List.iteri
. Queste funzioni vengono illustrate nell'esempio di codice seguente.
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
L'output è il seguente:
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
Un'altra funzione usata di frequente che trasforma gli elementi dell'elenco è List.map, che consente di applicare una funzione a ogni elemento di un elenco e di inserire tutti i risultati in un nuovo elenco. List.map2 e List.map3 sono varianti che accettano più elenchi. È anche possibile usare List.mapi e List.mapi2, se, oltre all'elemento , la funzione deve essere passata all'indice di ogni elemento. L'unica differenza tra List.mapi2
e List.mapi
consiste nel fatto che List.mapi2
usa due elenchi. Nell'esempio seguente viene illustrato List.map.
let list1 = [1; 2; 3]
let newList = List.map (fun x -> x + 1) list1
printfn "%A" newList
L'output è il seguente:
[2; 3; 4]
L'esempio seguente mostra l'uso di 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
L'output è il seguente:
[5; 7; 9]
L'esempio seguente mostra l'uso di List.map3
.
let newList2 = List.map3 (fun x y z -> x + y + z) list1 list2 [2; 3; 4]
printfn "%A" newList2
L'output è il seguente:
[7; 10; 13]
L'esempio seguente mostra l'uso di List.mapi
.
let newListAddIndex = List.mapi (fun i x -> x + i) list1
printfn "%A" newListAddIndex
L'output è il seguente:
[1; 3; 5]
L'esempio seguente mostra l'uso di List.mapi2
.
let listAddTimesIndex = List.mapi2 (fun i x y -> (x + y) * i) list1 list2
printfn "%A" listAddTimesIndex
L'output è il seguente:
[0; 7; 18]
List.collect è simile List.map
a , ad eccezione del fatto che ogni elemento produce un elenco e tutti questi elenchi vengono concatenati in un elenco finale. Nel codice seguente ogni elemento dell'elenco genera tre numeri, che vengono raccolti in un elenco.
let collectList = List.collect (fun x -> [for i in 1..3 -> x * i]) list1
printfn "%A" collectList
L'output è il seguente:
[1; 2; 3; 2; 4; 6; 3; 6; 9]
È anche possibile usare List.filter, che accetta una condizione booleana e produce un nuovo elenco costituito solo da elementi che soddisfano la condizione specificata.
let evenOnlyList = List.filter (fun x -> x % 2 = 0) [1; 2; 3; 4; 5; 6]
L'elenco risultante è [2; 4; 6]
.
Una combinazione di mapping e filtro, List.choose consente di trasformare e selezionare gli elementi contemporaneamente. List.choose
applica una funzione che restituisce un'opzione a ogni elemento di un elenco e restituisce un nuovo elenco dei risultati per gli elementi quando la funzione restituisce il valore di opzione Some
.
Il codice seguente illustra l'uso di List.choose
per selezionare parole in lettere maiuscole da un elenco di parole.
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
L'output è il seguente:
["Rome's"; "Bob's"]
Operazioni su più elenchi
Gli elenchi possono essere uniti. Per unire due elenchi in uno, usare List.append. Per unire più di due elenchi, usare 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
Operazioni di riduzione e analisi
Alcune operazioni sugli elenchi comportano interdipendenze tra tutti gli elementi di un elenco. Le operazioni di riduzione e analisi sono simili List.iter
a e List.map
in cui si richiama una funzione su ogni elemento, ma queste operazioni forniscono un parametro aggiuntivo denominato debugger che contiene informazioni tramite il calcolo.
Usare List.fold
per eseguire un calcolo in un elenco.
Nell'esempio di codice seguente viene illustrato l'uso di List.fold per eseguire varie operazioni.
L'elenco viene attraversato. L'accumulatore acc
è un valore passato contestualmente all'esecuzione del calcolo. Il primo argomento accetta l'accumulatore e l'elemento dell'elenco e restituisce il risultato provvisorio del calcolo per l'elemento dell'elenco. Il secondo argomento è il valore iniziale dell'accumulatore.
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])
Le versioni di queste funzioni che contengono una cifra nel nome della funzione consentono di eseguire operazioni su più di un elenco. Ad esempio, List.fold2 esegue calcoli in due elenchi.
L'esempio seguente illustra l'uso di 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
e List.scan differiscono in che List.fold
restituisce il valore finale del parametro aggiuntivo, ma List.scan
restituisce l'elenco dei valori intermedi (insieme al valore finale) del parametro aggiuntivo.
Ognuna di queste funzioni include una variante inversa, ad esempio List.foldBack, che differisce nell'ordine in cui l'elenco viene attraversato e l'ordine degli argomenti. Inoltre, List.fold
e List.foldBack
hanno varianti, List.fold2 e List.foldBack2, che accettano due elenchi di lunghezza uguale. La funzione eseguita su ogni elemento può usare elementi corrispondenti di entrambi gli elenchi per eseguire un'azione. I tipi di elementi dei due elenchi possono essere diversi, come nell'esempio seguente, in cui un elenco contiene importi di transazioni per un conto bancario e l'altro contiene il tipo di transazione (deposito o prelievo).
// 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
Per un calcolo quale una somma, List.fold
e List.foldBack
producono lo stesso effetto, poiché il risultato non dipende dall'ordine di attraversamento. Nell'esempio seguente viene usato List.foldBack
per aggiungere gli elementi a un elenco.
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])
Per l'esempio seguente viene nuovamente usato il conto bancario precedente. Questa volta viene aggiunto un nuovo tipo di transazione: il calcolo degli interessi. Il saldo finale dipende ora dall'ordine delle transazioni.
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
La funzione List.reduce è in qualche modo simile List.fold
a e List.scan
, ad eccezione del fatto che invece di passare intorno a un riduttore separato, List.reduce
accetta una funzione che accetta due argomenti del tipo di elemento invece di uno solo e uno di questi argomenti funge da riduttore, vale a dire che archivia il risultato intermedio del calcolo. List.reduce
esegue innanzitutto le operazioni sui primi due elementi dell'elenco, quindi usa il risultato dell'operazione insieme all'elemento successivo. Poiché nessun accumulatore separato ha un proprio tipo, List.reduce
può essere usato al posto di List.fold
solo se l'accumulatore e il tipo di elemento hanno lo stesso tipo. Il codice seguente illustra l'uso di List.reduce
. List.reduce
genera un'eccezione se l'elenco fornito non contiene elementi.
Nel codice seguente per la prima chiamata all'espressione lambda vengono forniti gli argomenti 2 e 4, quindi la chiamata restituisce 6. Per la chiamata successiva vengono forniti gli argomenti 6 e 10, pertanto il risultato è 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
Conversione tra elenchi e altri tipi di raccolta
Il modulo List
fornisce funzioni per la conversione da e verso sequenze e matrici. Per eseguire la conversione da o verso una sequenza, usare List.toSeq o List.ofSeq. Per eseguire la conversione da o verso una matrice, usare List.toArray o List.ofArray.
Operazioni aggiuntive
Per informazioni sulle operazioni aggiuntive sugli elenchi, vedere l'argomento di riferimento della libreria List Module.For information about additional operations on lists, see the library reference topic List Module.