Listas
Uma lista em F# é uma série imutável, ordenada, de elementos do mesmo tipo. Para realizar operações básicas em listas, use as funções do Módulo de listas.
Criando e inicializando listas
Você pode definir uma lista ao relacionar explicitamente os elementos, separados por ponto e vírgula e entre colchetes, como mostra a linha de código a seguir.
let list123 = [ 1; 2; 3 ]
Você também pode colocar quebras de linha entre os elementos, caso em que as vírgulas são opcionais. A última sintaxe pode resultar em um código mais legível quando as expressões de inicialização de elemento são mais longas ou quando você quer incluir um comentário para cada elemento.
let list123 = [ 1; 2; 3 ]
Normalmente, todos os elementos da lista devem ser do mesmo tipo. Uma exceção existe quando uma lista em que os elementos são especificados para ser um tipo de base pode ter elementos do tipo derivado. Dessa forma, o que segue é aceitável, porque tanto Button
quanto CheckBox
derivam de Control
.
let myControlList: Control list = [ new Button(); new CheckBox() ]
Você também pode definir os elementos da lista, usando um intervalo indicado por inteiros separados pelo operador de intervalo (..
), como mostrado no código a seguir.
let list1 = [ 1..10 ]
Uma lista vazia é especificada por um par de colchetes sem nada entre eles.
// An empty list.
let listEmpty = []
Você também pode usar uma expressão de sequência para criar uma lista. Confira Expressões de sequência para saber mais. Por exemplo, o código a seguir cria uma lista dos quadrados de números inteiros de 1 a 10.
let listOfSquares = [ for i in 1..10 -> i * i ]
Operadores para trabalhar com listas
Você pode anexar elementos a uma lista usando o operador (cons) ::
. Se list1
for [2; 3; 4]
, o seguinte código criará list2
como [100; 2; 3; 4]
.
let list2 = 100 :: list1
Você pode concatenar listas que possuem tipos compatíveis usando o operador @
, como no código a seguir. Se list1
for [2; 3; 4]
e list2
for [100; 2; 3; 4]
, esse código criará list3
como [2; 3; 4; 100; 2; 3; 4]
.
let list3 = list1 @ list2
As funções para operações em listas estão disponíveis no Módulo de listas.
Como as listas em F# são imutáveis, quaisquer operações de modificação geram novas listas em vez de modificar as listas existentes.
As listas em F# são implementadas como listas vinculadas individualmente, o que significa que as operações que acessam apenas o cabeçalho das listas são O(1) e o acesso de elemento é O(n).
Propriedades
O tipo de lista oferece suporte às seguintes propriedades:
Propriedade | Type | Descrição |
---|---|---|
Head | 'T |
O primeiro elemento. |
Empty (vazio) | 'T list |
A propriedade estática que retorna uma lista vazia do tipo apropriado. |
IsEmpty | bool |
true se a lista não tiver elementos. |
Item | 'T |
O elemento no índice especificado (com base em zero). |
Comprimento | int |
O número de elementos. |
Parte final | 'T list |
A lista sem o primeiro elemento. |
A seguir, alguns exemplos de como usar essas propriedades.
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))
Usando as listas
A programação com listas permite executar operações complexas com uma pequena quantidade de código. Esta seção descreve operações comuns em listas que são importantes para programação funcional.
Recursão com listas
As listas são particularmente adequadas para técnicas de programação recursivas. Considere uma operação que deve ser executada em cada elemento de uma lista. Você pode fazer isso de forma recursiva, operando no cabeçalho da lista e passando para a parte final da lista, que é uma lista menor que consiste na lista original, sem o primeiro elemento, e volte novamente ao próximo nível de recursão.
Para escrever uma função recursiva, você usa o operador cons (::
) em correspondência de padrões, o que permite separar o cabeçalho de uma lista da parte final da lista.
O exemplo de código a seguir mostra como usar a correspondência de padrões para implementar uma função recursiva que executa operações em uma lista.
let rec sum list =
match list with
| head :: tail -> head + sum tail
| [] -> 0
O código anterior funciona bem para pequenas listas, mas para listas maiores, pode estourar a pilha. O código a seguir melhora este código usando um argumento acumulador, uma técnica padrão para trabalhar com funções recursivas. O uso do argumento acumulador torna a parte final da função recursiva, o que economiza espaço em pilha.
let sum list =
let rec loop list acc =
match list with
| head :: tail -> loop tail (acc + head)
| [] -> acc
loop list 0
A função RemoveAllMultiples
é uma função recursiva que usa duas listas. A primeira lista contém os números cujos múltiplos serão removidos e a segunda lista é a lista a partir da qual os números serão removidos. O código no exemplo a seguir usa essa função recursiva para eliminar todos os números não primos de uma lista, deixando uma lista de números primos como o resultado.
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)
A saída é da seguinte maneira:
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]
Funções do módulo
O Módulo de listas fornece funções que acessam os elementos de uma lista. O elemento cabeçalho é o mais rápido e fácil de acessar. Use a propriedade Head ou a função de módulo List.head. É possível acessar a parte final de uma lista usando a propriedade Tail da função List.tail. Para encontrar um elemento por índice, use a função List.nth. List.nth
percorre a lista. Portanto, é O(n). Se seu código usa List.nth
com frequência, considere o uso de uma matriz em vez de uma lista. O acesso de elemento em matrizes é O(1).
Operações boolianas em listas
A função List.isEmpty determina se uma lista tem elementos ou não.
A função List.exists aplica um teste booliano aos elementos de uma lista e retorna true
se algum deles atende ao teste. List.exists2 é semelhante, mas opera em pares sucessivos de elementos em duas listas.
O código a seguir demonstra o 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)
A saída é da seguinte maneira:
For list [0; 1; 2; 3], contains zero is true
O exemplo a seguir demonstra o 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
A saída é da seguinte maneira:
Lists [1; 2; 3; 4; 5] and [5; 4; 3; 2; 1] have at least one equal element at the same position.
É possível usar List.forall para testar se todos os elementos de uma lista atendem a uma condição.
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])
A saída é da seguinte maneira:
true
false
Da mesma forma, List.forall2 determina se todos os elementos nas posições correspondentes em duas listas atendem a uma expressão booliana que envolve 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])
A saída é da seguinte maneira:
true
false
Operações de classificação em listas
As funções List.sort, List.sortBy e List.sortWith classificam listas. A função de classificação determina qual dessas três funções usar. List.sort
usa uma comparação genérica padrão. A comparação genérica utiliza operadores globais com base na função de comparação genérica para comparar valores. Funciona de forma eficiente com uma grande variedade de tipos de elementos, tais como tipos numéricos simples, tuplas, registros, uniões discriminadas, listas, matrizes e qualquer tipo que implementa System.IComparable
. Para os tipos que implementam System.IComparable
, a comparação genérica usa a função System.IComparable.CompareTo()
. A comparação genérica também funciona com cadeias de caracteres, mas usa uma ordem de classificação independente de cultura. A comparação genérica não deve ser usada em tipos sem suporte, como os tipos de função. Além disso, o desempenho da comparação genérica padrão é melhor para tipos estruturados pequenos; para tipos estruturados maiores que precisam ser comparados e classificados com frequência, considere implementar System.IComparable
e fornecer uma implementação eficiente do método System.IComparable.CompareTo()
.
List.sortBy
usa uma função que retorna um valor usado como critério de classificação e List.sortWith
assume uma função de comparação como argumento. Essas duas últimas funções são úteis quando você está trabalhando com tipos que não suportam comparação ou quando a comparação requer mais semântica de comparação complexa, como é o caso das cadeias de caracteres com reconhecimento de cultura.
O exemplo a seguir demonstra o uso de List.sort
.
let sortedList1 = List.sort [1; 4; 8; -2; 5]
printfn "%A" sortedList1
A saída é da seguinte maneira:
[-2; 1; 4; 5; 8]
O exemplo a seguir demonstra o uso de List.sortBy
.
let sortedList2 = List.sortBy (fun elem -> abs elem) [1; 4; 8; -2; 5]
printfn "%A" sortedList2
A saída é da seguinte maneira:
[1; -2; 4; 5; 8]
O exemplo a seguir demonstra o uso de List.sortWith
. Neste exemplo, a função de comparação personalizada compareWidgets
é usada para comparar um primeiro campo de um tipo personalizado e depois outro, quando os valores do primeiro campo são iguais.
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
A saída é da seguinte maneira:
[{ID = 92;
Rev = 1;}; {ID = 92;
Rev = 1;}; {ID = 100;
Rev = 2;}; {ID = 100;
Rev = 5;}; {ID = 110;
Rev = 1;}]
Operações de pesquisa em listas
Várias operações de pesquisa têm suporte para listas. A mais simples, List.find, permite encontrar o primeiro elemento que corresponde a uma determinada condição.
O exemplo de código a seguir demonstra o uso de List.find
para encontrar o primeiro número divisível por 5 em uma lista.
let isDivisibleBy number elem = elem % number = 0
let result = List.find (isDivisibleBy 5) [ 1 .. 100 ]
printfn "%d " result
O resultado é 5.
Se for preciso transformar primeiro os elementos, chame List.pick, que usa uma função que retorna uma opção e procura o primeiro valor de opção, que é Some(x)
. Em vez de retornar o elemento, List.pick
retorna o resultado x
. Se nenhum elemento correspondente for encontrado, List.pick
lançará System.Collections.Generic.KeyNotFoundException
. O código a seguir demonstra o 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
A saída é da seguinte maneira:
"b"
Outro grupo de operações de pesquisa, List.tryFind e funções relacionadas, retorna um valor de opção. A função List.tryFind
retorna o primeiro elemento de uma lista que satisfaz a uma condição se tal elemento existir, mas retorna o valor da opção None
se o elemento não existir. A variação List.tryFindIndex retorna o índice do elemento, se encontrado, em vez do próprio elemento. Essas funções são ilustradas no código a seguir.
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."
A saída é da seguinte maneira:
The first even value is 22.
The first even value is at position 8.
Operações aritméticas em listas
Operações aritméticas comuns, como soma e média, são incorporadas ao Módulo de listas. Para trabalhar com List.sum, o tipo de elemento de lista deve dar suporte ao operador +
e ter um valor zero. Todos os tipos aritméticos internos satisfazem a essas condições. Para trabalhar com List.average, o tipo de elemento deve dar suporte à divisão sem restante, o que exclui tipos integrais, mas permite tipos de ponto flutuante. As funções List.sumBy e List.averageBy utilizam uma função como um parâmetro e os resultados dessa função são usados para calcular os valores da soma ou da média.
O código a seguir demonstra o uso de 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
A saída é 1.000000
.
O código a seguir demonstra o uso de List.averageBy
.
let avg2 = List.averageBy (fun elem -> float elem) [1 .. 10]
printfn "%f" avg2
A saída é 5.5
.
Listas e tuplas
As listas que contêm tuplas podem ser manipuladas pelas funções de compactação e descompactação. Essas funções combinam duas listas de valores únicos em uma lista de tuplas ou separam uma lista de tuplas em duas listas de valores únicos. A função List.zip mais simples utiliza duas listas de elementos únicos e produz uma lista única de pares de tupla. Outra versão, List.zip3, usa três listas de elementos únicos e produz uma única lista de tuplas de três elementos. O exemplo de código a seguir demonstra o uso de List.zip
.
let list1 = [ 1; 2; 3 ]
let list2 = [ -1; -2; -3 ]
let listZip = List.zip list1 list2
printfn "%A" listZip
A saída é da seguinte maneira:
[(1, -1); (2, -2); (3; -3)]
O exemplo de código a seguir demonstra o uso de List.zip3
.
let list3 = [ 0; 0; 0]
let listZip3 = List.zip3 list1 list2 list3
printfn "%A" listZip3
A saída é da seguinte maneira:
[(1, -1, 0); (2, -2, 0); (3, -3, 0)]
As versões correspondentes não compactadas, List.unzip e List.unzip3, utilizam listas de tuplas e retornam listas em uma tupla, em que a primeira lista contém todos os primeiros elementos em cada tupla, a segunda contém os segundos elementos de cada tupla e assim por diante.
O exemplo de código a seguir demonstra o uso de List.unzip.
let lists = List.unzip [(1,2); (3,4)]
printfn "%A" lists
printfn "%A %A" (fst lists) (snd lists)
A saída é da seguinte maneira:
([1; 3], [2; 4])
[1; 3] [2; 4]
O exemplo de código a seguir demonstra o uso de List.unzip3.
let listsUnzip3 = List.unzip3 [(1,2,3); (4,5,6)]
printfn "%A" listsUnzip3
A saída é da seguinte maneira:
([1; 4], [2; 5], [3; 6])
Operando em elementos de lista
F# oferece suporte a uma variedade de operações em elementos de lista. A mais simples é List.iter, que permite que você chame uma função em cada elemento de uma lista. As variações incluem List.iter2, que permite executar uma operação em elementos de duas listas, List.iteri, que é semelhante a List.iter
, exceto pelo fato de que o índice de cada elemento é transmitido como um argumento à função que é chamada para cada elemento, e List.iteri2, que é uma combinação da funcionalidade de List.iter2
e List.iteri
. O exemplo de código a seguir ilustra essas funções.
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
A saída é da seguinte maneira:
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
Outra função usada frequentemente que transforma os elementos de lista é List.map, que permite aplicar uma função a cada elemento de uma lista e colocar todos os resultados em uma nova lista. List.map2 e List.map3 são variações que usam várias listas. Também é possível usar List.mapi e List.mapi2 quando, além do elemento, é preciso transmitir à função o índice de cada elemento. A única diferença entre List.mapi2
e List.mapi
é que List.mapi2
funciona com as duas listas. O exemplo a seguir ilustra List.map.
let list1 = [1; 2; 3]
let newList = List.map (fun x -> x + 1) list1
printfn "%A" newList
A saída é da seguinte maneira:
[2; 3; 4]
O exemplo a seguir mostra o 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
A saída é da seguinte maneira:
[5; 7; 9]
O exemplo a seguir mostra o uso de List.map3
.
let newList2 = List.map3 (fun x y z -> x + y + z) list1 list2 [2; 3; 4]
printfn "%A" newList2
A saída é da seguinte maneira:
[7; 10; 13]
O exemplo a seguir mostra o uso de List.mapi
.
let newListAddIndex = List.mapi (fun i x -> x + i) list1
printfn "%A" newListAddIndex
A saída é da seguinte maneira:
[1; 3; 5]
O exemplo a seguir mostra o uso de List.mapi2
.
let listAddTimesIndex = List.mapi2 (fun i x y -> (x + y) * i) list1 list2
printfn "%A" listAddTimesIndex
A saída é da seguinte maneira:
[0; 7; 18]
List.collect é como List.map
, exceto pelo fato de que cada elemento produz uma lista e todas essas listas são concatenadas em uma lista final. No código a seguir, cada elemento da lista gera três números. Eles são todos coletados em uma única lista.
let collectList = List.collect (fun x -> [for i in 1..3 -> x * i]) list1
printfn "%A" collectList
A saída é da seguinte maneira:
[1; 2; 3; 2; 4; 6; 3; 6; 9]
Também é possível usar List.filter, que utiliza uma condição booliana e produz uma nova lista que consiste apenas em elementos que atendem à condição fornecida.
let evenOnlyList = List.filter (fun x -> x % 2 = 0) [1; 2; 3; 4; 5; 6]
A lista resultante é [2; 4; 6]
.
List.choose é uma combinação de mapa e filtro que permite transformar e selecionar elementos ao mesmo tempo. List.choose
aplica uma função que retorna uma opção para cada elemento de uma lista e retorna uma nova lista de resultados para os elementos quando a função retorna o valor de opção Some
.
O código a seguir demonstra o uso de List.choose
para selecionar palavras em maiúsculas a partir de uma lista de palavras.
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
A saída é da seguinte maneira:
["Rome's"; "Bob's"]
Operando em várias listas
As listas podem ser reunidas. Para unir duas listas em uma só, use List.append. Para unir mais de duas listas, use 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
Operações de dobra e digitalização
Algumas operações de lista envolvem interdependências entre todos os elementos da lista. As operações de dobra e exame são como List.iter
e List.map
, pois você invoca uma função em cada elemento, mas essas operações fornecem o parâmetro adicional accumulator, que carrega informações por meio de cálculos.
Use List.fold
para executar um cálculo em uma lista.
O exemplo de código a seguir demonstra o uso de List.fold para executar várias operações.
A lista é percorrida; o acumulador acc
é um valor passado conforme ocorre o progresso do cálculo. O primeiro argumento usa o acumulador e o elemento da lista, retornando o resultado intermediário do cálculo para esse elemento da lista. O segundo argumento é o valor inicial do 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])
As versões dessas funções que têm um dígito no nome da função operam em mais de uma lista. Por exemplo, List.fold2 realiza cálculos em duas listas.
O exemplo a seguir demonstra o 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
e List.scan diferem no sentido de que List.fold
retorna o valor final do parâmetro adicional, mas List.scan
retorna a lista dos valores intermediários (além do valor final) do parâmetro adicional.
Cada uma dessas funções inclui uma variação inversa, por exemplo, List.foldBack, que difere na ordem em que a lista é percorrida e na ordem dos argumentos. Além disso, List.fold
e List.foldBack
têm variações, List.fold2 e List.foldBack2, que utilizam duas listas de igual tamanho. A função executada em cada elemento pode usar elementos correspondentes das duas listas para realizar alguma ação. Os tipos de elementos das duas listas podem ser diferentes, como no exemplo a seguir, em que uma lista contém valores de transação de uma conta bancária e a outra lista contém o tipo de transação: depósito ou retirada.
// 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 um cálculo como somatória, List.fold
e List.foldBack
têm o mesmo efeito, porque o resultado não depende da ordem de passagem. No exemplo a seguir, List.foldBack
é usado para adicionar os elementos em uma lista.
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])
O exemplo a seguir retorna ao exemplo da conta bancária. Desta vez, um novo tipo de transação é adicionado: cálculo de juros. O saldo final agora depende da ordem das transações.
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
A função List.reduce é, de alguma forma, como List.fold
e List.scan
, mas em vez de realizar a transmissão para um acumulador separado, List.reduce
utiliza uma função que usa dois argumentos do tipo de elemento, em vez de apenas um, em que um deles funciona como acumulador, o que significa que ele armazena o resultado intermediário do cálculo. List.reduce
começa a operar nos dois primeiros elementos da lista e, em seguida, usa o resultado da operação juntamente com o próximo elemento. Como não há um acumulador separado que tem o seu próprio tipo, List.reduce
pode ser usado no lugar de List.fold
somente quando o acumulador e o tipo de elemento têm o mesmo tipo. O código a seguir demonstra o uso de List.reduce
. List.reduce
lançará uma exceção se a lista fornecida não tiver elementos.
No código a seguir, a primeira chamada para a expressão lambda recebeu os argumentos 2 e 4, retornando 6 e a chamada seguinte recebeu os argumentos 6 e 10, de modo que o resultado é 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
Convertendo entre listas e outros tipos de coleção
O módulo List
fornece funções para converter de e para sequências e matrizes. Para converter de/em uma sequência, use List.toSeq ou List.ofSeq. Para converter de/em uma matriz, use List.toArray ou List.ofArray.
Operações adicionais
Para saber mais sobre operações adicionais em listas, confira o tópico Módulo de listas da referência da biblioteca.