Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
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 punto e virgola e racchiusi tra parentesi quadre, come illustrato nella riga di codice seguente.
let list123 = [ 1; 2; 3 ]
È anche possibile inserire interruzioni di riga tra gli elementi, nel qual caso i punti e virgola sono facoltativi. La seconda sintassi può comportare codice più leggibile quando le espressioni di inizializzazione degli elementi sono più lunghe o quando 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. Un'eccezione è che un elenco in cui gli elementi vengono specificati come tipo di base può avere elementi che sono tipi derivati. Di conseguenza, il codice seguente è accettabile, perché sia Button
che CheckBox
derivano da Control
.
let myControlList: Control list = [ new Button(); new CheckBox() ]
È anche possibile definire gli elementi elenco usando un intervallo indicato da numeri interi separati dall'operatore di intervallo (..
), come illustrato nel codice seguente.
let list1 = [ 1..10 ]
Un elenco vuoto viene specificato da una coppia di parentesi quadre con nessun elemento tra loro.
// An empty list.
let listEmpty = []
È anche possibile usare un'espressione di sequenza per creare un elenco. Per altre informazioni, vedere Espressioni di sequenza . Ad esempio, il codice seguente crea un elenco di quadrati di interi da 1 a 10.
let listOfSquares = [ for i in 1..10 -> i * i ]
Operatori per l'utilizzo degli elenchi
È possibile collegare 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 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, le operazioni di modifica generano nuovi elenchi anziché modificare gli elenchi 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à | TIPO | Descrizione |
---|---|---|
Testa | 'T |
Primo elemento. |
vuoto | 'T list |
Proprietà statica che restituisce un elenco vuoto del tipo appropriato. |
IsEmpty | bool |
true se l'elenco non contiene elementi. |
elemento | 'T |
Elemento in corrispondenza dell'indice specificato (in base zero). |
Lunghezza | int |
Numero di elementi. |
Tail | 'T list |
Elenco senza il primo elemento. |
Di seguito sono riportati 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))
Uso degli elenchi
La programmazione con elenchi consente di eseguire operazioni complesse con una piccola quantità di codice. In questa sezione vengono descritte le operazioni comuni sugli elenchi importanti per la programmazione funzionale.
Ricorsione con elenchi
Gli elenchi sono adatti in modo univoco alle tecniche di programmazione ricorsive. Si consideri un'operazione che deve essere eseguita su ogni elemento di un elenco. È possibile eseguire questa operazione in modo ricorsivo operando sull'intestazione dell'elenco e quindi passando la parte finale dell'elenco, ovvero un elenco più piccolo costituito dall'elenco originale senza il primo elemento, di nuovo al livello successivo di ricorsione.
Per scrivere una funzione ricorsiva di questo tipo, usare l'operatore cons (::
) nei criteri di ricerca, che consente di separare l'intestazione di un elenco dalla parte finale.
Nell'esempio di codice seguente viene illustrato come usare criteri di ricerca per implementare una funzione ricorsiva che esegue operazioni su un elenco.
let rec sum list =
match list with
| head :: tail -> head + sum tail
| [] -> 0
Il codice precedente funziona bene per gli elenchi di piccole dimensioni, ma per gli elenchi più grandi potrebbe sovrapporsi allo stack. Il codice seguente migliora questo codice usando un argomento enumeratore, una tecnica standard per l'uso di funzioni ricorsive. L'uso dell'argomento sdk rende ricorsiva la coda della funzione, che consente di risparmiare spazio nello stack.
let sum list =
let rec loop list acc =
match list with
| head :: tail -> loop tail (acc + head)
| [] -> acc
loop list 0
La funzione è una funzione RemoveAllMultiples
ricorsiva che accetta due elenchi. Il primo elenco contiene i numeri i cui multipli verranno rimossi e il secondo elenco è 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, lasciando 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 del modulo
Il modulo List fornisce funzioni che accedono agli elementi di un elenco. L'elemento head è il più veloce e più semplice da 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
spesso, è consigliabile usare una matrice anziché 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
Nell'esempio seguente viene illustrato 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 quale di queste tre funzioni usare.
List.sort
usa il confronto generico predefinito. Il confronto generico usa operatori globali basati sulla funzione di confronto generica per confrontare i valori. Funziona in modo efficiente 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 System.IComparable.CompareTo()
funzione . Il confronto generico funziona anche con le stringhe, ma usa un ordinamento indipendente da impostazioni cultura. Il confronto generico non deve essere usato su tipi non supportati, ad esempio i tipi di funzione. Inoltre, le prestazioni del confronto generico predefinito sono ottimali per i tipi strutturati di piccole dimensioni; per i tipi strutturati di dimensioni maggiori che devono essere confrontati e ordinati frequentemente, è consigliabile implementare System.IComparable
e fornire un'implementazione efficiente del System.IComparable.CompareTo()
metodo .
List.sortBy
accetta una funzione che restituisce un valore utilizzato come criterio di ordinamento e List.sortWith
accetta una funzione di confronto come argomento. Queste ultime due funzioni sono utili quando si utilizzano tipi che non supportano il confronto o quando il confronto richiede una semantica di confronto più complessa, come nel caso di stringhe con riconoscimento delle impostazioni cultura.
Nell'esempio seguente viene illustrato 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]
Nell'esempio seguente viene illustrato 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]
Nell'esempio seguente viene illustrato l'uso di List.sortWith
. In questo esempio, la funzione compareWidgets
di confronto personalizzata viene usata per confrontare prima un campo di un tipo personalizzato e un altro quando 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
Sono supportate numerose operazioni di ricerca per gli elenchi. Il più semplice, List.find, consente di trovare il primo elemento che corrisponde a una determinata condizione.
Nell'esempio di codice seguente viene illustrato 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 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 List.tryFind
funzione restituisce il primo elemento di un elenco che soddisfa una condizione se tale elemento esiste, ma il valore None
dell'opzione in caso contrario. La variante List.tryFindIndex restituisce l'indice dell'elemento, se presente, anziché l'elemento stesso. Queste funzioni sono 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 predefiniti 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 zip e decomprime. Queste funzioni combinano due elenchi di valori singoli in un elenco di tuple o separano un elenco di tuple in due elenchi di singoli valori. La funzione List.zip più semplice accetta due elenchi di singoli elementi e produce un unico 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. Nell'esempio di codice seguente viene illustrato 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)]
Nell'esempio di codice seguente viene illustrato 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])
Funzionamento sugli elementi elenco
F# supporta un'ampia gamma di operazioni sugli elementi dell'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
. 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 è 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
è che List.mapi2
funziona con 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. Questi vengono tutti raccolti in un unico 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 Some
dell'opzione .
Nel codice seguente viene illustrato l'uso di List.choose
per selezionare parole maiuscole all'esterno di 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"]
Funzionamento 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 di elenco comportano interdipendenze tra tutti gli elementi dell'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.
Utilizzare 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 durante il calcolo. Il primo argomento accetta l'elemento e l'elemento list e restituisce il risultato provvisorio del calcolo per tale elemento di 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 con una cifra nel nome della funzione operano su più elenchi. Ad esempio, List.fold2 esegue calcoli in due elenchi.
Nell'esempio seguente viene illustrato 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 gli elementi corrispondenti di entrambi gli elenchi per eseguire un'azione. I tipi di elemento dei due elenchi possono essere diversi, come nell'esempio seguente, in cui un elenco contiene gli importi delle transazioni per un conto bancario, e l'altro elenco 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 come la somma List.fold
e List.foldBack
avere lo stesso effetto perché il risultato non dipende dall'ordine di attraversamento. Nell'esempio List.foldBack
seguente viene usato per aggiungere gli elementi in 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])
Nell'esempio seguente viene restituito l'esempio di conto bancario. Questa volta viene aggiunto un nuovo tipo di transazione: un calcolo di interesse. 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
inizia operando sui primi due elementi dell'elenco e quindi usa il risultato dell'operazione insieme all'elemento successivo. Poiché non esiste unelemento separato che ha un proprio tipo, List.reduce
può essere utilizzato al posto di List.fold
solo quando l'elemento 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 la prima chiamata all'espressione lambda viene assegnata agli argomenti 2 e 4 e restituisce 6 e alla chiamata successiva vengono assegnati gli argomenti 6 e 10, quindi 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 List
modulo fornisce funzioni per la conversione in e da 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.