Elenchi (F#)
Un elenco in F# è una serie ordinata e non modificabile di elementi dello stesso tipo.
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 ]
È inoltre 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 sia CheckBox derivano da Control.
let myControlList : Control list = [ new Button(); new CheckBox() ]
È inoltre possibile definire elementi di elenco tramite un intervallo indicato da Integer separati dall'operatore di intervallo (..), come indicato nel codice seguente.
let list1 = [ 1 .. 10 ]
È inoltre possibile definire un elenco tramite un costrutto ciclico, come nel codice seguente.
let listOfSquares = [ for i in 1 .. 10 -> i*i ]
Un elenco vuoto viene specificato da una coppia di parentesi quadre al cui interno non è presente alcun elemento.
// An empty list.
let listEmpty = []
È inoltre possibile utilizzare un'espressione sequenza per creare un elenco. Vedere "Espressioni sequenza" in Sequenze. Nell'esempio di codice seguente viene creato un elenco di quadrati di Integer compresi tra 1 e 10.
let squaresList = [ for i in 1 .. 10 -> i * i ]
Operatori per l'utilizzo di elenchi
È possibile associare elementi a un elenco utilizzando l'operatore (cons) ::. Se list1 è [2; 3; 4], il codice seguente crea list2 come [100; 2; 3; 4].
let list2 = 100 :: list1
È possibile concatenare elenchi che dispongono di tipi compatibili tramite l'operatore @, come nel codice seguente. Se list1 è [2; 3; 4] e list2 è [100; 2; 3; 4 ], il codice crea list3 come [2; 3; 4; 100; 2; 3; 4].
let list3 = list1 @ list2
Nel modulo List sono disponibili funzioni per l'esecuzione di operazioni sugli elenchi.
Poiché gli elenchi in F# non sono modificabili, qualsiasi operazione di modifica comporta la generazione di nuovi elenchi e non la modifica di quelli esistenti.
Gli elenchi in F# vengono implementati come elenchi collegati singolarmente, ovvero le operazioni che accedono solo all'elemento head dell'elenco sono di tipo O(1) e l'accesso all'elemento è di tipo O(n).
Proprietà
Il tipo elenco supporta le proprietà seguenti:
Proprietà |
Tipo |
Descrizione |
---|---|---|
'T |
Primo elemento. |
|
'T list |
Proprietà statica che restituisce un elenco vuoto del tipo appropriato. |
|
bool |
true se nell'elenco non sono presenti elementi. |
|
'T |
Elemento in corrispondenza dell'indice specificato (base zero). |
|
int |
Numero di elementi. |
|
'T list |
Elenco senza il primo elemento. |
Di seguito sono illustrati alcuni esempi di utilizzo 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))
Utilizzo di elenchi
Utilizzando gli elenchi nella programmazione è possibile eseguire operazioni complesse con una piccola quantità di codice. In questa sezione vengono descritte 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, 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 utilizzare l'operatore cons (::) nei criteri di ricerca, per separare l'elemento head dell'elenco dall'elemento tail.
Nell'esempio di codice seguente viene illustrato come utilizzare la corrispondenza dei modelli per implementare una funzione ricorsiva che consenta di eseguire operazioni in 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 altri elenchi potrebbe verificarsi un overflow dello stack. Il codice seguente migliora questo tipo di codice mediante un argomento dell'accumulatore, una tecnica standard per l'utilizzo di funzioni ricorsive. L'utilizzo dell'argomento dell'accumulatore rende ricorsivo l'elemento tail della funzione, che risparmia spazio dello stack.
let sum list =
let rec loop list acc =
match list with
| head :: tail -> loop tail (acc + head)
| [] -> acc
loop list 0
La funzione RemoveAllMultiples è una funzione ricorsiva che utilizza due elenchi. Il primo elenco contiene i numeri i cui multipli verranno rimossi e il secondo contiene i numeri da rimuovere. Nel codice di esempio seguente viene utilizzata 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 è indicato di seguito:
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 del 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. Utilizzare la proprietà Head o la funzione di modulo List.head. È possibile accedere all'elemento tail di un elenco tramite la proprietà Tail o la funzione List.tail. Per cercare un elemento tramite l'indice, utilizzare la funzione List.nth. List.nth attraversa l'elenco e pertanto è O(n). Se nel codice viene utilizzato List.nth in modo frequente, è opportuno considerare l'utilizzo di una matrice anziché di un elenco. L'accesso dell'elemento nelle matrici è O(1).
Operazioni booleane negli elenchi
La funzione List.isEmpty consente di determinare se un elenco include elementi.
La funzione List.exists applica un test booleano agli elementi di un elenco e restituisce true se un elemento qualsiasi soddisfa il test. List.exists2 è analoga, ma opera su coppie consecutive di elementi in due elenchi.
Nell'esempio di codice seguente viene illustrato l'utilizzo 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 è indicato di seguito:
For list [0; 1; 2; 3], contains zero is true
Nell'esempio seguente viene illustrato l'utilizzo 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 è indicato di seguito:
Lists [1; 2; 3; 4; 5] and [5; 4; 3; 2; 1] have at least one equal element at the same position.
È possibile utilizzare List.forall nel caso in cui si desideri 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 è indicato di seguito:
true
false
In modo analogo, List.forall2 determina se tutti gli elementi nelle posizioni corrispondenti dei 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 è indicato di seguito:
true
false
Operazioni di ordinamento negli elenchi
Le funzioni List.sort, List.sortBye List.sortWith ordinano elenchi. La funzione di ordinamento determina quali funzioni utilizzare tra le tre indicate in precedenza. List.sort utilizza il confronto generico predefinito. Il confronto generico utilizza operatori globali basati sulla funzione di confronto generico per confrontare 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 IComparable. Per i tipi che implementano IComparable, il confronto generico utilizza la funzione CompareTo. Il confronto generico utilizza anche le stringhe, ma ricorre a un ordinamento indipendente dalle impostazioni cultura. Non è opportuno utilizzare 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 gli altri tipi, che devono essere confrontati e ordinati con frequenza, considerare l'implementazione di IComparable e fornire un'implementazione efficiente del metodo CompareTo.
List.sortBy utilizza una funzione che restituisce un valore utilizzato come criterio di ordinamento e List.sortWith utilizza una funzione di confronto come argomento. Queste ultime due funzioni risultano utili nel caso in cui si utilizzino tipi che non supportano il confronto oppure se il confronto richiede una semantica più complessa, come nel caso di stringhe dipendenti dalle impostazioni cultura.
Nell'esempio seguente viene illustrato l'utilizzo di List.sort.
let sortedList1 = List.sort [1; 4; 8; -2; 5]
printfn "%A" sortedList1
L'output è indicato di seguito:
[-2; 1; 4; 5; 8]
Nell'esempio seguente viene illustrato l'utilizzo di List.sortBy.
let sortedList2 = List.sortBy (fun elem -> abs elem) [1; 4; 8; -2; 5]
printfn "%A" sortedList2
L'output è indicato di seguito:
[1; -2; 4; 5; 8]
Nell'esempio seguente viene illustrato l'utilizzo di List.sortWith. In questo esempio la funzione di confronto personalizzato compareWidgets viene utilizzata 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 è indicato di seguito:
[{ID = 92;
Rev = 1;}; {ID = 92;
Rev = 1;}; {ID = 100;
Rev = 2;}; {ID = 100;
Rev = 5;}; {ID = 110;
Rev = 1;}]
Operazioni di ricerca negli elenchi
Per gli elenchi sono supportate numerose operazioni di ricerca. La più semplice, List.find, consente di cercare il primo elemento corrispondente a una determinata condizione.
Nell'esempio di codice seguente viene illustrato l'utilizzo di List.find per cercare 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 è in primo luogo necessario trasformare gli elementi, chiamare List.pick, che utilizza una funzione che restituisce un'opzione e cerca il primo valore di opzione corrispondente a Some(x). Anziché restituire l'elemento, List.pick restituisce il risultato x. Se non viene rilevato alcun elemento corrispondente, List.pick genera un'eccezione KeyNotFoundException. Nell'esempio di codice riportato di seguito viene illustrato l'utilizzo 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 è indicato di seguito:
"b"
Un altro gruppo di operazioni di ricerca, costituito da List.tryFind e dalle 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 variazione List.tryFindIndex restituisce l'indice dell'elemento, se ne viene rilevato uno, 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 è indicato di seguito:
The first even value is 22.
The first even value is at position 8.
Operazioni aritmetiche negli elenchi
Le operazioni aritmetiche comuni, quali la somma e la media, sono incorporate nel modulo List. Per utilizzare List.sum, il tipo di elemento dell'elenco deve supportare l'operatore + e avere un valore zero. Tutti i tipi aritmetici incorporati soddisfano queste condizioni. Per utilizzare List.average, il tipo di elemento deve supportare la divisione senza resto. Sono pertanto esclusi i tipi integrali e consentiti i tipi a virgola mobile. Le funzioni List.sumBy e List.averageBy utilizzano una funzione come parametro e i risultati della funzione vengono utilizzati per calcolare i valori per la somma o la media.
Nell'esempio di codice seguente viene illustrato l'utilizzo 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.
Nell'esempio di codice seguente viene illustrato l'utilizzo 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, utilizza due elenchi di elementi singoli e produce un unico elenco di coppie di tuple. Un'altra versione, List.zip3, utilizza tre elenchi di elementi singoli e produce un solo elenco di tuple con tre elementi. Nell'esempio di codice seguente viene illustrato l'utilizzo di List.zip.
let list1 = [ 1; 2; 3 ]
let list2 = [ -1; -2; -3 ]
let listZip = List.zip list1 list2
printfn "%A" listZip
L'output è indicato di seguito:
[(1, -1); (2, -2); (3; -3)]
Nell'esempio di codice seguente viene illustrato l'utilizzo di List.zip3.
let list3 = [ 0; 0; 0]
let listZip3 = List.zip3 list1 list2 list3
printfn "%A" listZip3
L'output è indicato di seguito:
[(1, -1, 0); (2, -2, 0); (3, -3, 0)]
Le versioni decompresse corrispondenti, List.unzip e List.unzip3, utilizzano elenchi di tuple e restituiscono elenchi in una tupla, in cui il primo elenco contiene tutti i primi elementi di ogni tupla, il secondo elenco contiene i secondi elementi di ogni tupla e così via.
Nell'esempio di codice seguente viene illustrato l'utilizzo di List.unzip.
let lists = List.unzip [(1,2); (3,4)]
printfn "%A" lists
printfn "%A %A" (fst lists) (snd lists)
L'output è indicato di seguito:
([1; 3], [2; 4])
[1; 3] [2; 4]
Nell'esempio di codice seguente viene illustrato l'utilizzo di List.unzip3.
let listsUnzip3 = List.unzip3 [(1,2,3); (4,5,6)]
printfn "%A" listsUnzip3
L'output è indicato di seguito:
([1; 4], [2; 5], [3; 6])
Operazioni sugli elementi degli elenchi
F# supporta un'ampia gamma di operazioni sugli elementi di elenco. La più semplice è List.iter, che consente di chiamare una funzione su ogni elemento di un elenco. Le variazioni includono List.iter2, che consente di eseguire un'operazione sugli elementi di due elenchi, List.iteri, analoga a List.iter, ad eccezione del fatto che l'indice di ogni elemento viene passato come argomento alla funzione chiamata per ogni elemento, e List.iteri2, una combinazione delle funzionalità di List.iter2 e List.iteri. Nell'esempio di codice seguente vengono illustrate queste funzioni.
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 è indicato di seguito:
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 di frequente utilizzo per la trasformazione di elementi di elenco è List.map, che consente di applicare una funzione a ogni elemento di un elenco e di includere tutti i risultati in un nuovo elenco. List.map2 e List.map3 sono variazioni che utilizzano più elenchi. È inoltre possibile utilizzare List.mapi e List.mapi2 se per la funzione è necessario passare l'indice di ogni elemento, oltre all'elemento stesso. L'unica differenza tra List.mapi2 e List.mapi consiste nel fatto che List.mapi2 utilizza due elenchi. Nell'esempio riportato di seguito viene illustrato List.map.
let list1 = [1; 2; 3]
let newList = List.map (fun x -> x + 1) list1
printfn "%A" newList
L'output è indicato di seguito:
[2; 3; 4]
Nell'esempio riportato di seguito viene illustrato l'utilizzo 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 è indicato di seguito:
[5; 7; 9]
Nell'esempio riportato di seguito viene illustrato l'utilizzo di List.map3.
let newList2 = List.map3 (fun x y z -> x + y + z) list1 list2 [2; 3; 4]
printfn "%A" newList2
L'output è indicato di seguito:
[7; 10; 13]
Nell'esempio riportato di seguito viene illustrato l'utilizzo di List.mapi.
let newListAddIndex = List.mapi (fun i x -> x + i) list1
printfn "%A" newListAddIndex
L'output è indicato di seguito:
[1; 3; 5]
Nell'esempio riportato di seguito viene illustrato l'utilizzo di List.mapi2.
let listAddTimesIndex = List.mapi2 (fun i x y -> (x + y) * i) list1 list2
printfn "%A" listAddTimesIndex
L'output è indicato di seguito:
[0; 7; 18]
List.collect è analogo a List.map, 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 è indicato di seguito:
[1; 2; 3; 2; 4; 6; 3; 6; 9]
È inoltre possibile utilizzare List.filter, che utilizza una condizione booleana e produce un nuovo elenco costituito dai soli 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].
List.choose, una combinazione di mappa e filtro, consente di trasformare e selezionare elementi contemporaneamente. List.choose applica una funzione che restituisce un'opzione a ogni elemento di un elenco, nonché un nuovo elenco dei risultati per gli elementi nel caso in cui la funzione restituisca il valore di opzione Some.
Nel codice seguente viene illustrato l'utilizzo 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 è indicato di seguito:
["Rome's"; "Bob's"]
Operazioni su più elenchi
Gli elenchi possono essere uniti. Per unire due elenchi, utilizzare List.append. Per unire più di due elenchi, utilizzare 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 negli elenchi comportano interdipendenze tra tutti gli elementi dell'elenco. Le operazioni di riduzione e analisi sono analoghe a List.iter e List.map, in quanto viene richiamata una funzione in ogni elemento, ma queste operazioni forniscono un parametro aggiuntivo, denominato accumulatore, che trasmette informazioni nel calcolo.
Utilizzare List.fold per eseguire un calcolo in un elenco.
Nell'esempio di codice seguente viene illustrato l'utilizzo di List.fold per eseguire diverse operazioni.
L'elenco viene attraversato. L'accumulatore acc è un valore passato contestualmente all'esecuzione del calcolo. Il primo argomento utilizza 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. List.fold2 esegue ad esempio calcoli su due elenchi.
Nell'esempio seguente viene illustrato l'utilizzo 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 quanto List.fold restituisce il valore finale del parametro aggiuntivo, mentre List.scan restituisce l'elenco dei valori intermedi (insieme al valore finale).
Ognuna di queste funzioni include una variazione inversa, ad esempio, List.foldBack, che differisce per quanto riguarda l'ordine di attraversamento dell'elenco e l'ordine degli argomenti. List.fold e List.foldBack presentano inoltre variazioni, List.fold2 e List.foldBack2, che utilizzano due elenchi di uguale lunghezza. La funzione eseguita su ogni elemento può utilizzare 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 utilizzato List.foldBack per aggiungere gli elementi a un elenco.
let sumListBack list = List.foldBack (fun acc elem -> acc + elem) list 0
printfn "%d" (sumListBack [1; 2; 3])
// For a calculation in which the order of traversal is important, fold and foldBack have different
// results. For example, replacing fold with foldBack in the listReverse function
// produces a function that copies the list, rather than reversing it.
let copyList list = List.foldBack (fun elem acc -> elem::acc) list []
printfn "%A" (copyList [1 .. 10])
Nell'esempio riportato di seguito viene nuovamente utilizzato il conto bancario precedente. Questa volta viene aggiunta una nuova transazione: un calcolo degli interessi. Il bilancio 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 è più o meno analoga a List.fold e List.scan, ad eccezione del fatto che, anziché utilizzare un accumulatore separato, List.reduce utilizza una funzione che preleva due argomenti del tipo di elemento anziché uno e uno di questi viene utilizzato come accumulatore, ovvero archivia il risultato intermedio del calcolo. List.reduce viene avviato operando in primo luogo sui primi due elementi dell'elenco, quindi utilizza il risultato dell'operazione insieme all'elemento successivo. Poiché nessun accumulatore separato dispone di un proprio tipo, List.reduce può essere utilizzato al posto di List.fold solo se l'accumulatore e il tipo di elemento dispongono dello stesso tipo. Nell'esempio di codice seguente viene illustrato l'utilizzo 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, la chiamata restituisce 6 e 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 insieme
Il modulo List fornisce funzioni per la conversione da e verso sequenze e matrici. Per eseguire una conversione da o verso una sequenza, utilizzare List.toSeq o List.ofSeq. Per eseguire una conversione da o verso una matrice, utilizzare List.toArray o List.ofArray.
Opzioni aggiuntive
Per informazioni su ulteriori operazioni eseguibili sugli elenchi, vedere l'argomento di riferimento della libreria Modulo Collections.List (F#).
Vedere anche
Riferimenti
Altre risorse
Riferimenti per il linguaggio F#
Cronologia delle modifiche
Data |
Cronologia |
Motivo |
---|---|---|
Ottobre 2010 |
Correzione dell'output di uno degli esempi di codice. |
Commenti e suggerimenti dei clienti. |
Aprile 2011 |
Correzione delle informazioni relative alla proprietà Empty nella sezione Proprietà. |
Commenti e suggerimenti dei clienti. |