Condividi tramite


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 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.sumBye 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.mapa , 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 Somedell'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.

Vedere anche