Share via


Elenchi (F#)

Un elenco in F# è una serie ordinata e non modificabile di elementi dello stesso tipo. Per eseguire le operazioni di base sugli elenchi, usare le funzioni nel form List.

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 ]

È anche possibile definire un elenco usando un costrutto di ciclo, 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 = []

È possibile anche usare un'espressione sequenza per creare un elenco. Vedere "Espressioni sequenza" in Sequenze. Ad esempio, il codice seguente crea un elenco di quadrati di numeri interi compresi tra 1 e 10.

let squaresList = [ 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

Nel form List sono disponibili le 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 anziché 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 di elenco supporta le proprietà seguenti:

Proprietà

Tipo

Descrizione

Head

'T

Primo elemento.

Empty

'T list

Proprietà statica che restituisce un elenco vuoto del tipo appropriato.

IsEmpty

bool

true se nell'elenco non sono presenti elementi.

Item

'T

Elemento in corrispondenza dell'indice specificato (a base zero).

Length

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 è 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 di modulo

Il modulo List fornisce le 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 di modulo List.head. È possibile accedere all'elemento tail di un elenco tramite la proprietà Tail o la funzione List.tail. Per trovare un elemento in base all'indice, usare la funzione List.nth. List.nth attraversa l'elenco e 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 qualsiasi soddisfa il test. La funzione List.exists2 è analoga, ma opera su coppie consecutive 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 è indicato di seguito:

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 è 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 usare 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 sugli elenchi

Le funzioni List.sort, List.sortBy e List.sortWith ordinano gli elenchi. 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 IComparable. Per i tipi che implementano IComparable, il confronto generico usa la funzione 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 IComparable e a fornire un'implementazione efficiente del metodo 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 è indicato di seguito:

[-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 è indicato di seguito:

[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 è 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 sugli elenchi

Per gli elenchi sono supportate numerose operazioni di ricerca. La più semplice, List.find, consente di trovare il primo elemento che corrisponde a una condizione specifica.

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 è in primo luogo necessario trasformare gli elementi, chiamare List.pick, che accetta 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 trovato alcun elemento corrispondente, List.pick genera un'eccezione 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 è 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 trovato 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 sugli elenchi

Le operazioni aritmetiche comuni, quali la somma e la media, sono incorporate nel modulo List. Per usare 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 usare List.average, il tipo di elemento deve supportare la divisione senza resto. Sono pertanto esclusi i tipi integrali ma consentiti i tipi a virgola mobile. Le funzioni List.sumBy e List.averageBy accettano una funzione come parametro e i risultati della 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 più semplice, List.zip, accetta due elenchi di elementi singoli e produce un unico elenco di coppie di tuple. Un'altra versione, List.zip3, accetta tre elenchi di elementi singoli e produce un solo 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 è indicato di seguito:

[(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 è indicato di seguito:

[(1, -1, 0); (2, -2, 0); (3, -3, 0)]

Le versioni decompresse corrispondenti, List.unzip e List.unzip3, accettano 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.

L'esempio di codice seguente illustra 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 è indicato di seguito:

([1; 3], [2; 4])
[1; 3] [2; 4]

L'esempio di codice seguente illustra l'uso 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 dell'elenco

F# supporta un'ampia gamma di operazioni sugli elementi di un elenco. L'operazione 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. 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 è 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 usata di frequente per la trasformazione degli elementi di un 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 accettano più elenchi. È anche possibile usare 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 usa due elenchi. L'esempio seguente illustra 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]

L'esempio seguente illustra 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 è indicato di seguito:

[5; 7; 9]

L'esempio seguente illustra 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 è indicato di seguito:

[7; 10; 13]

L'esempio seguente illustra l'uso di List.mapi.

let newListAddIndex = List.mapi (fun i x -> x + i) list1
printfn "%A" newListAddIndex

L'output è indicato di seguito:

[1; 3; 5]

L'esempio seguente illustra l'uso 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]

È possibile usare anche List.filter, che accetta una condizione booleana e produce un nuovo elenco costituito solo dagli 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 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 è indicato di seguito:

["Rome's"; "Bob's"]

Operazioni su più elenchi

Gli elenchi possono essere uniti. Per unire due elenchi, 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 analoghe a List.iter e List.map, in quanto viene richiamata una funzione per ogni elemento, ma queste operazioni forniscono un parametro aggiuntivo, denominato accumulatore, che trasmette informazioni nel calcolo.

Usare List.fold per eseguire un calcolo in un elenco.

L'esempio di codice seguente illustra l'uso 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 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. List.fold2 esegue ad esempio calcoli su 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 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 accettano due elenchi di uguale lunghezza. 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 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])

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 è più o meno analoga a List.fold e List.scan, ad eccezione del fatto che, anziché passare un accumulatore separato, List.reduce accetta una funzione che usa due argomenti del tipo di elemento anziché uno e uno di questi funge da accumulatore, ovvero 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 una conversione da o verso una sequenza, usare List.toSeq o List.ofSeq. Per eseguire una conversione da o verso una matrice, usare List.toArray o List.ofArray.

Operazioni aggiuntive

Per informazioni su altre operazioni che è possibile eseguire sugli elenchi, vedere l'argomento di riferimento della libreria Modulo Collections.List (F#).

Vedere anche

Riferimenti

Sequenze (F#)

Opzioni (F#)

Altre risorse

Riferimenti per il linguaggio F#

Tipi F#

Matrici (F#)