Listas (F#)

En F#, una lista es una serie ordenada e inmutable de elementos del mismo tipo.

Crear e inicializar listas

Para definir una lista, se pueden enumerar explícitamente los elementos, separándolos por signos de punto y coma y escribiéndolos entre corchetes, tal y como se muestra en la siguiente línea de código.

let list123 = [ 1; 2; 3 ]

También se pueden separar los elementos mediante saltos de línea, en cuyo caso los signos de punto y coma son opcionales. Esta última sintaxis puede facilitar la lectura del código cuando las expresiones de inicialización de elementos son más largas o cuando se desea incluir un comentario para cada elemento.

let list123 = [
    1
    2
    3 ]

Normalmente, todos los elementos de una lista deben ser del mismo tipo. Hay una excepción: una lista cuyos elementos deben ser tipos base puede tener elementos que son tipos derivados. Por consiguiente, el siguiente código es aceptable porque Button y CheckBox se derivan de Control.

let myControlList : Control list = [ new Button(); new CheckBox() ]

Para definir los elementos de una lista, también se puede usar un intervalo indicado por enteros que van separados por el operador de intervalo (..), tal y como se muestra en el siguiente código.

let list1 = [ 1 .. 10 ]

Para definir una lista, también se puede utilizar una construcción de bucle, como en el siguiente código.

let listOfSquares = [ for i in 1 .. 10 -> i*i ]

Una lista vacía se especifica mediante un par de corchetes que no tengan nada entre ellos.

// An empty list.
let listEmpty = []

También se puede utilizar una expresión de secuencia para crear una lista. Vea la sección "Expresiones de secuencia" en Secuencias. Por ejemplo, el siguiente código crea una lista de cuadrados de los enteros del 1 al 10.

let squaresList = [ for i in 1 .. 10 -> i * i ]

Operadores para trabajar con listas

Para adjuntar elementos a una lista, se puede usar el operador :: (cons). Si list1 es [2; 3; 4], el siguiente código crea list2 como [100; 2; 3; 4].

let list2 = 100 :: list1

Para concatenar listas que tienen tipos compatibles, se usa el operador @, como en el siguiente código. Si list1 es [2; 3; 4] y list2 es [100; 2; 3; 4 ], este código crea list3 como [2; 3; 4; 100; 2; 3; 4].

let list3 = list1 @ list2

Las funciones para realizar operaciones con listas están disponibles en el módulo List.

Dado que en F# las listas son inmutables, cualquier operación de modificación genera listas nuevas en lugar de modificar las existentes.

En F#, las listas se implementan como listas con vinculación única, lo que significa que las operaciones que tienen acceso únicamente al encabezado de la lista son O(1) y el acceso a los elementos es O(n).

Propiedades

El tipo de lista admite las propiedades siguientes:

Propiedad

Tipo

Descripción

Head

'T

El primer elemento.

Vacío

'T list

Propiedad estática que devuelve una lista vacía del tipo adecuado.

IsEmpty

bool

Es true si la lista no tiene ningún elemento.

Elemento

'T

Es el elemento en el índice (de base cero) especificado.

Longitud

int

El número de elementos.

Tail

'T list

Es la lista sin el primer elemento.

A continuación figuran algunos ejemplos del uso de estas propiedades.

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))

Utilizar listas

Programar con listas permite realizar operaciones complejas con una cantidad reducida de código. En esta sección, se describen algunas operaciones comunes que se realizan con listas y que son importantes para la programación funcional.

Recursividad con listas

Las listas satisfacen singularmente las necesidades de las técnicas de programación recursivas. Tomemos una operación que se debe realizar con cada elemento de una lista. Se puede hacer de forma recursiva realizando la operación con el encabezado de la lista y, a continuación, pasando la cola de la lista (que es la lista original sin el primer elemento) al nivel de recursividad siguiente.

Para escribir este tipo de función recursiva, se utiliza el operador cons (::) en la coincidencia de modelos, lo que permite separar el encabezado de una lista de su cola.

En el ejemplo de código siguiente se muestra cómo utilizar la coincidencia de modelos para implementar una función recursiva que realiza operaciones en una lista.

let rec sum list =
   match list with
   | head :: tail -> head + sum tail
   | [] -> 0

El código anterior funciona bien para listas pequeñas, pero para listas de mayor tamaño, podría causar un desbordamiento de la pila. El siguiente código es mejor que el anterior ya que se usa un argumento acumulador, que es una técnica estándar para utilizar funciones recursivas. Al usar un argumento acumulador, la cola de la función se vuelve recursiva, por lo que se ahorra espacio en la pila.

let sum list =
   let rec loop list acc =
       match list with
       | head :: tail -> loop tail (acc + head)
       | [] -> acc
   loop list 0

La función RemoveAllMultiples es una función recursiva que toma dos listas. La primera lista contiene los números cuyos múltiplos se van a quitar, y la segunda es la lista de la que se van a quitar los números. En el siguiente código de ejemplo, se utiliza esta función recursiva para eliminar todos los números no primos de una lista y dejar, como resultado, una lista de números primos.

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)

La salida es la siguiente:

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]

Funciones de módulo

El módulo List proporciona funciones que obtienen acceso a los elementos de una lista. El acceso al elemento de encabezado es el más rápido y más fácil de realizar. Para ello, se utiliza la propiedad Head o la función de módulo List.head. Para obtener acceso a la cola de una lista, se usa la propiedad Tail o la función List.tail. Para buscar un elemento por su índice, se utiliza la función List.nth. List.nth recorre la lista. Por consiguiente, es O(n). Si el código usa List.nth con frecuencia, puede considerar la posibilidad de usar una matriz en lugar de una lista. El acceso a los elementos de una matriz es O(1).

Operaciones booleanas con listas

La función List.isEmpty determina si una lista tiene elementos o no.

La función List.exists aplica una prueba booleana a los elementos de una lista y devuelve true si algún elemento supera la prueba. La función List.exists2 es similar pero se utiliza con pares de elementos sucesivos de dos listas.

En el código siguiente, se muestra el uso de 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)

La salida es la siguiente:

For list [0; 1; 2; 3], contains zero is true

El siguiente ejemplo muestra el uso de 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

La salida es la siguiente:

Lists [1; 2; 3; 4; 5] and [5; 4; 3; 2; 1] have at least one equal element at the same position.

Se puede utilizar List.forall para comprobar si todos los elementos de una lista cumplen una condición.

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])

La salida es la siguiente:

true
false

De forma similar, List.forall2 determina si todos los elementos situados en las posiciones correspondientes de dos listas cumplen una expresión booleana que se aplica a cada par de elementos.

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])

La salida es la siguiente:

true
false

Operaciones de ordenación con listas

Las funciones List.sort, List.sortBy y List.sortWith se utilizan para ordenar listas. La función de ordenación determina cuál de estas tres funciones se va a utilizar. List.sort utiliza la comparación genérica predeterminada. En la comparación genérica, se usan operadores globales basados en la función de comparación genérica para comparar valores. Es eficaz con una gran variedad de tipos de elementos, como tipos numéricos simples, tuplas, registros, uniones discriminadas, listas, matrices y cualquier tipo que implemente IComparable. Para los tipos que implementan IComparable, la comparación genérica utiliza la función CompareTo. La comparación genérica también se puede usar con cadenas, pero el criterio de ordenación es independiente de la referencia cultural. La comparación genérica no debe usarse con tipos no compatibles, como los tipos de función. Además, la comparación genérica predeterminada tiene un rendimiento óptimo en el caso de tipos estructurados de tamaño reducido; para la comparación y la ordenación de tipos estructurados de mayor tamaño, considere la posibilidad de implementar IComparable y proporcionar una implementación eficaz del método CompareTo.

List.sortBy toma una función que devuelve un valor que se utiliza como criterio de ordenación; List.sortWith toma una función de comparación como argumento. Estas dos últimas funciones son útiles cuando se usan tipos que no admiten la comparación o cuando la comparación requiere una semántica más compleja, como en el caso de cadenas que tienen en cuenta la referencia cultural.

En el siguiente ejemplo, se muestra el uso de List.sort.

let sortedList1 = List.sort [1; 4; 8; -2; 5]
printfn "%A" sortedList1

La salida es la siguiente:

[-2; 1; 4; 5; 8]

En el siguiente ejemplo se muestra el uso de List.sortBy.

let sortedList2 = List.sortBy (fun elem -> abs elem) [1; 4; 8; -2; 5]
printfn "%A" sortedList2

La salida es la siguiente:

[1; -2; 4; 5; 8]

En el siguiente ejemplo se muestra el uso de List.sortWith. En este ejemplo, se utiliza la función de comparación personalizada compareWidgets para comparar primero un campo de tipo personalizado y, a continuación, otro cuando los valores del primer campo son iguales.

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

La salida es la siguiente:

  [{ID = 92;
    Rev = 1;}; {ID = 92;
                Rev = 1;}; {ID = 100;
                            Rev = 2;}; {ID = 100;
                                        Rev = 5;}; {ID = 110;
                                                    Rev = 1;}]

Operaciones de búsqueda con listas

Las listas admiten numerosas operaciones de búsqueda. La más sencilla, List.find, permite buscar el primer elemento que cumpla una condición determinada.

En el siguiente ejemplo de código, se muestra el uso de List.find para buscar en una lista el primer número que sea divisible por 5.

let isDivisibleBy number elem = elem % number = 0
let result = List.find (isDivisibleBy 5) [ 1 .. 100 ]
printfn "%d " result

El resultado es 5.

Si es preciso transformar previamente los elementos, se llama a List.pick, que toma una función que devuelve una opción, y busca el primer valor de opción que sea Some(x). En lugar de devolver el elemento, List.pick devuelve el valor de x resultante. Si no se encuentra ningún elemento correspondiente, List.pick produce la excepción KeyNotFoundException. En el código siguiente, se muestra el uso de 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

La salida es la siguiente:

"b"

Otro grupo de operaciones de búsqueda, como List.tryFind y otras funciones relacionadas, devuelven un valor de opción. La función List.tryFind devuelve el primer elemento de una lista que cumple una condición si ese elemento existe; de lo contrario, devuelve el valor de opción None. La variación List.tryFindIndex devuelve el índice del elemento si se encuentra alguno, en lugar del propio elemento. Estas funciones se reflejan en el código siguiente.

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."

La salida es la siguiente:

The first even value is 22.
The first even value is at position 8.

Operaciones aritméticas con listas

Las operaciones aritméticas comunes, como la suma o el cálculo del promedio, están integradas en el módulo List. Para poder usar List.sum, el tipo de elemento debe admitir el operador + y tener un valor cero. Todos los tipos aritméticos integrados cumplen estas condiciones. Para poder utilizar List.average, el tipo de elemento debe admitir la división sin resto, lo que excluye los tipos enteros pero permite los de punto flotante. Las funciones List.sumBy y List.averageBy toman una función como parámetro; los resultados de esta función se utilizan para calcular los valores de la suma o del promedio.

En el código siguiente, se muestra el uso de List.sum, List.sumBy y 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

El resultado es 1.000000

En el código siguiente, se muestra el uso de List.averageBy.

let avg2 = List.averageBy (fun elem -> float elem) [1 .. 10]
printfn "%f" avg2

El resultado es 5.5

Listas y tuplas

Las listas que contienen tuplas pueden manipularse mediante funciones de compresión y descompresión. Estas funciones combinan dos listas de valores individuales en una sola lista de tuplas o dividen una lista de tuplas en dos listas de valores individuales. La función List.zip más simple toma dos listas de elementos individuales y genera una sola lista de pares de tupla. Otra versión, List.zip3, toma tres listas de elementos individuales y genera una sola lista de tuplas que contienen tres elementos. En el ejemplo de código siguiente, se muestra el uso de List.zip.

let list1 = [ 1; 2; 3 ]
let list2 = [ -1; -2; -3 ]
let listZip = List.zip list1 list2
printfn "%A" listZip

La salida es la siguiente:

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

En el ejemplo de código siguiente, se muestra el uso de List.zip3.

let list3 = [ 0; 0; 0]
let listZip3 = List.zip3 list1 list2 list3
printfn "%A" listZip3

La salida es la siguiente:

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

Las versiones unzip correspondientes (List.unzip y List.unzip3) toman listas de tuplas y devuelven listas en una tupla, donde la primera lista contiene todos los elementos que aparecían en primer lugar en cada tupla, la segunda lista contiene los segundos elementos de cada tupla, y así sucesivamente.

En el ejemplo de código siguiente, se muestra el uso de List.unzip.

let lists = List.unzip [(1,2); (3,4)]
printfn "%A" lists
printfn "%A %A" (fst lists) (snd lists)

La salida es la siguiente:

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

En el ejemplo de código siguiente, se muestra el uso de List.unzip3.

let listsUnzip3 = List.unzip3 [(1,2,3); (4,5,6)]
printfn "%A" listsUnzip3

La salida es la siguiente:

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

Operar con elementos de lista

F# admite diversas operaciones con los elementos de una lista. La más simple es List.iter, que permite llamar a una función para cada elemento de una lista. Sus variaciones incluyen List.iter2, que permite realizar una operación con los elementos de dos listas; List.iteri, que es similar a List.iter excepto en que el índice de cada elemento se pasa como argumento de la función que se invoca para cada elemento; y List.iteri2, que combina la funcionalidad de List.iter2 y List.iteri. En el ejemplo de código siguiente, se muestran estas funciones.

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

La salida es la siguiente:

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

Otra función que se utiliza con frecuencia para transformar los elementos de una lista es List.map, que permite aplicar una función a cada elemento de una lista y colocar todos los resultados en una nueva lista. List.map2 y List.map3 son variaciones que toman varias listas. Asimismo, se pueden utilizar List.mapi y List.mapi2 si, además del elemento, también se debe pasar a la función el índice de cada elemento. La única diferencia entre List.mapi2 y List.mapi reside en que List.mapi2 se utiliza con dos listas. En el siguiente ejemplo, se muestra el uso de List.map.

let list1 = [1; 2; 3]
let newList = List.map (fun x -> x + 1) list1
printfn "%A" newList

La salida es la siguiente:

[2; 3; 4]

En el siguiente ejemplo, se muestra el uso de 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

La salida es la siguiente:

[5; 7; 9]

En el siguiente ejemplo, se muestra el uso de List.map3.

let newList2 = List.map3 (fun x y z -> x + y + z) list1 list2 [2; 3; 4]
printfn "%A" newList2

La salida es la siguiente:

[7; 10; 13]

En el siguiente ejemplo, se muestra el uso de List.mapi.

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

La salida es la siguiente:

[1; 3; 5]

En el siguiente ejemplo, se muestra el uso de List.mapi2.

let listAddTimesIndex = List.mapi2 (fun i x y -> (x + y) * i) list1 list2
printfn "%A" listAddTimesIndex

La salida es la siguiente:

[0; 7; 18]

List.collect es similar a List.map, excepto en que cada elemento genera una lista y todas estas listas se concatenan en una lista final. En el siguiente código, cada elemento de la lista genera tres números. Todos estos números se recopilan en una sola lista.

let collectList = List.collect (fun x -> [for i in 1..3 -> x * i]) list1
printfn "%A" collectList

La salida es la siguiente:

[1; 2; 3; 2; 4; 6; 3; 6; 9]

También se puede utilizar List.filter, que toma una condición booleana y genera una nueva lista que consta únicamente de los elementos que cumplen la condición especificada.

let evenOnlyList = List.filter (fun x -> x % 2 = 0) [1; 2; 3; 4; 5; 6]

La lista resultante es [2; 4; 6].

List.choose, que es una combinación de las funciones map y filter, permite transformar y seleccionar elementos a la vez. List.choose aplica una función que devuelve una opción a cada elemento de una lista y, a continuación, devuelve una nueva lista con los resultados de los elementos para los cuales la función devuelve el valor de opción Some.

En el siguiente código, se muestra el uso de List.choose para seleccionar en una lista las palabras que están en mayúsculas.

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

La salida es la siguiente:

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

Operar con varias listas

Las listas se pueden combinar. Para combinar dos listas en una, se utiliza List.append. Para combinar más de dos listas, se utiliza 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

Operaciones de acumulación fold y scan

Algunas operaciones con listas utilizan las interdependencias entre todos los elementos de ellas. Las operaciones de acumulación fold y scan son como List.iter y List.map porque invocan una función para cada elemento; sin embargo, estas operaciones proporcionan un parámetro adicional, denominado acumulador, que transporta información a lo largo de todo el cálculo.

Se utiliza List.fold para realizar un cálculo con una lista.

En el ejemplo de código siguiente, se muestra el uso de List.fold para realizar varias operaciones.

Se recorre la lista; el acumulador acc es un valor que se va pasando a medida que se realiza el cálculo. El primer argumento toma el acumulador y el elemento de la lista; a continuación, devuelve el resultado provisional del cálculo para ese elemento de la lista. El segundo argumento es el valor inicial del acumulador.

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])

Las versiones de estas funciones que tienen un dígito en el nombre de la función operan con más de una lista. Por ejemplo, List.fold2 realiza cálculos con dos listas.

El siguiente ejemplo muestra el uso de 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 y List.scan se diferencian en que List.fold devuelve el valor final del parámetro adicional, mientras que List.scan devuelve la lista de valores intermedios (así como el valor final) del parámetro adicional.

Cada una de estas funciones incluye una variación inversa; por ejemplo, List.foldBack, que se diferencia de la primera en el orden con que se recorre la lista y en el orden de los argumentos. Asimismo, List.fold y List.foldBack tienen variaciones, List.fold2 y List.foldBack2, que toman dos listas de la misma longitud. La función que se ejecuta en cada elemento puede utilizar los elementos correspondientes de ambas listas para realizar alguna acción. Los tipos de elemento de las dos listas pueden ser diferentes, como en el ejemplo siguiente, donde una lista contiene los importes de las transacciones de una cuenta bancaria, y la otra, el tipo de transacción: ingreso o disposición de fondos.

// 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

Para un cálculo como la suma, List.fold y List.foldBack tienen el mismo efecto porque el resultado no depende del orden de recorrido. En el siguiente ejemplo, se utiliza List.foldBack para sumar los elementos de una lista.

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])

En el siguiente ejemplo, se retoma el ejemplo de la cuenta bancaria. Esta vez, se agrega un nuevo tipo de transacción: un cálculo de intereses. En este caso, el saldo final sí depende del orden de las transacciones.

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 función List.reduce es similar a List.fold y List.scan; sin embargo, en lugar de pasar un acumulador independiente, List.reduce toma una función que toma dos argumentos del tipo de elemento en lugar de solo uno, y uno de esos argumentos actúa como acumulador, lo que significa que almacena el resultado intermedio del cálculo. List.reduce se aplica primero a los dos primeros elementos de la lista y, a continuación, utiliza el resultado de la operación junto con el elemento siguiente. Dado que no hay ningún acumulador independiente que tenga su propio tipo, se puede usar List.reduce en lugar de List.fold únicamente cuando el acumulador y el tipo de elemento tienen el mismo tipo. En el código siguiente, se muestra el uso de List.reduce. List.reduce produce una excepción si la lista proporcionada no tiene ningún elemento.

En el siguiente código, la primera llamada a la expresión lambda tiene 2 y 4 como argumentos y devuelve 6; la llamada siguiente tiene los argumentos 6 y 10, por lo que el resultado es 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

Convertir listas a otros tipos de colección y viceversa

El módulo List proporciona las funciones que permiten convertir listas en secuencias y matrices, y viceversa. Para convertir una lista en una secuencia o viceversa, se utiliza List.toSeq o List.ofSeq. Para convertir una lista en una matriz o viceversa, se utiliza List.toArray o List.ofArray.

Operaciones adicionales

Para obtener información sobre otras operaciones con listas, vea el tema de referencia de la biblioteca Collections.List (Módulo de F#).

Vea también

Referencia

Secuencias (F#)

Opciones (F#)

Otros recursos

Referencia del lenguaje F#

Tipos en F#

Matrices (F#)

Historial de cambios

Fecha

Historial

Motivo

Octubre de 2010

Se ha corregido la salida de uno de los ejemplos de código.

Comentarios de los clientes.

1 de abril de 2011

Se ha corregido la información de la propiedad Empty en la sección Propiedades.

Comentarios de los clientes.