Списки (F#)
Список в языке F# — это упорядоченный, неизменный ряд элементов одного типа.
Создание и инициализация списков
Список можно определить, явно перечислив его элементы между квадратными скобками с точкой с запятой в качестве разделителя, как показано в следующей строке кода.
let list123 = [ 1; 2; 3 ]
Можно также вставить между элементами разрывы строк; в этом случае точки с запятой являются необязательными.Последний вариант синтаксиса может повысить удобочитаемость кода, особенно если выражения инициализации элементов имеют значительную длину, или если для каждого элемента требуется ввести комментарий.
let list123 = [
1
2
3 ]
Обычно все элементы списка должны быть одного типа.Исключение заключается в том, что список, для которого указаны элементы базового типа, может содержать элементы производных типов.Таким образом приведенный ниже код является допустимым, поскольку типы Button и CheckBox являются производными от типа Control.
let myControlList : Control list = [ new Button(); new CheckBox() ]
Элементы списка можно также определить, используя диапазон, заданный целыми числами, разделенными оператором диапазона (..), как показано в следующем коде.
let list1 = [ 1 .. 10 ]
Также можно задать список с помощью циклической конструкции, как в следующем коде.
let listOfSquares = [ for i in 1 .. 10 -> i*i ]
Пустой список задается парой квадратных скобок, между которыми ничего нет.
// An empty list.
let listEmpty = []
Для создания списка можно также использовать выражение последовательности.См. пункт "Выражения последовательностей" в разделе Последовательности.Например, следующий код создает список квадратов целых чисел от 1 до 10.
let squaresList = [ for i in 1 .. 10 -> i * i ]
Операторы для работы со списками
Элементы можно добавить в список с помощью оператора :: (cons).Если список list1 содержит [2; 3; 4], следующий код создает список list2, содержащий [100; 2; 3; 4].
let list2 = 100 :: list1
Списки, имеющие совместимые типы, можно сцепить с помощью оператора @, как в следующем коде.Если список list1 содержит [2; 3; 4], а список list2 содержит [100; 2; 3; 4 ], этот код создает список list3, содержащий [2; 3; 4; 100; 2; 3; 4].
let list3 = list1 @ list2
Функции, выполняющие операции со списками, доступны в модуле List.
Поскольку списки в языке F# неизменяемы, в результате всех операций изменения создаются новые списки, а не изменяются старые.
Списки в языке F# реализованы в виде однократно связанных списков, что означает, что операции, обращающиеся только к началу списка, имеют порядок O(1), а операции, обращающиеся к элементу, — O(n).
Свойства
Список поддерживает следующие свойства.
Свойство. |
Тип |
Описание |
---|---|---|
'T |
Первый элемент. |
|
'T list |
Статическое свойство, которое возвращает пустой список соответствующего типа. |
|
bool |
Значение true, если список не содержит элементов. |
|
'T |
Элемент с указанным индексом (начинающимся с нуля). |
|
int |
Количество элементов. |
|
'T list |
Список без первого элемента. |
Ниже приведено несколько примеров использования этих свойств.
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))
Использование списков
Программирование с использованием списков позволяет выполнять сложные операции с помощью небольших фрагментов кода.В этом разделе описаны стандартные операции со списками, важные для функционального программирования.
Рекурсия с использованием списков
Списки уникальным образом подходят для применения методов рекурсивного программирования.Рассмотрим операцию, которую необходимо выполнить с каждым элементом списка.Ее можно выполнить рекурсивно, сначала применив операцию к первому элементу списка (head), а затем передав на следующий уровень оставшуюся часть списка (tail), которая представляет собой список меньшей длины, содержащий исходный список без первого элемента.
Для записи такой рекурсивной функции при сопоставлении шаблона используется оператор cons (::), что позволяет отделить первый элемент списка от его остальной части.
В следующем примере кода показано, как с помощью сопоставления шаблона реализовать рекурсивную функцию, которая выполняет операции со списком.
let rec sum list =
match list with
| head :: tail -> head + sum tail
| [] -> 0
Предыдущий код хорошо работает с небольшими списками, но для больших списков он может привести к переполнению стека.Следующий код улучшает данный код с помощью накапливающего аргумента — стандартного способа для работы с рекурсивными функциями.Использование накапливающего аргумента превращает функцию tail в рекурсивную, что экономит место в стеке.
let sum list =
let rec loop list acc =
match list with
| head :: tail -> loop tail (acc + head)
| [] -> acc
loop list 0
Функции RemoveAllMultiples — это рекурсивная функция, принимающая два списка.Первый список содержит числа, кратные которым необходимо удалить, а второй список является списком, из которого требуется удалить числа.В коде следующего примера рекурсивная функция используется для исключения из списка всех чисел, которые не являются простыми, в результате чего будет получен список, целиком состоящий из простых чисел.
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)
Выходные данные выглядят следующим образом.
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]
Функции модуля
Модуль List предоставляет функции для доступа к элементам списка.Проще и быстрее всего обратиться к первому элементу.Используйте свойство Head или функцию модуля List.head.Для доступа к хвосту списка используйте свойство Tail или функцию List.tail.Чтобы найти элемент по его индексу, используйте функцию List.nth.Функция List.nth выполняет перебор списка.Поэтому она имеет порядок O(n).Если в коде часто используется функция List.nth, возможно, имеет смысл использовать вместо списка массив.Обращение к элементу массива имеет порядок O(1).
Логические операции в списках
Функция List.isEmpty определяет, имеются ли в списке какие-либо элементы.
Функция List.exists выполняет логическую проверку над элементами списка и возвращает значение true, если какой-либо элемент удовлетворяет заданному условию.Функция List.exists2 аналогична, но работает с последовательными парами элементов в двух списках.
В следующем коде демонстрируется использование функции 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)
Выходные данные выглядят следующим образом.
For list [0; 1; 2; 3], contains zero is true
Следующий пример демонстрирует использование 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
Выходные данные выглядят следующим образом.
Lists [1; 2; 3; 4; 5] and [5; 4; 3; 2; 1] have at least one equal element at the same position.
Чтобы проверить, удовлетворяют ли все элементы списка определенному условию, можно воспользоваться функцией List.forall.
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])
Выходные данные выглядят следующим образом.
true
false
Аналогично, функция List.forall2 определяет, все ли элементы в соответствующих позициях в двух списках удовлетворяют логическому условию, в котором используется каждая пара элементов.
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])
Выходные данные выглядят следующим образом.
true
false
Операции сортировки в списках
Функции List.sort, List.sortBy и List.sortWith служат для сортировки списков.Функция сортировки определяет, какую их этих трех функций следует использовать.В функции List.sort используется универсальное сравнение по умолчанию.При универсальном сравнении для сравнения величин используются глобальные операторы, основанные на универсальной функции сравнения.Такое сравнение эффективно для широкого круга типов элементов, таких как простые численные типы, кортежи, записи, размеченные объединения, списки, массивы и любые типы, реализующие IComparable.Для типов, реализующих IComparable, при универсальном сравнении используется функция CompareTo.Универсальное сравнение также работает со строками, но в нем используется порядок сортировки, зависящий от языка и региональных параметров.Универсальное сравнение не следует использовать с неподдерживаемыми типами, такими как типы функций.Кроме того, производительность универсального сравнения по умолчанию оптимальна для небольших структурированных типов; для больших структурированных типов, которые требуется часто сравнивать и сортировать, рекомендуется реализовать IComparable и предоставить эффективную реализацию метода CompareTo.
Функция List.sortBy принимает функцию, возвращающую значение, используемое в качестве критерия сортировки, а функция List.sortWith принимает функцию сравнения в качестве аргумента.Эти две последние функции удобны при работе с типами, не поддерживающими сравнение, или когда сравнение требует более сложной семантики, как в случае строк, зависящих от языка и региональных параметров.
В следующем примере показано использование функции List.sort.
let sortedList1 = List.sort [1; 4; 8; -2; 5]
printfn "%A" sortedList1
Выходные данные выглядят следующим образом.
[-2; 1; 4; 5; 8]
В следующем примере показано использование функции List.sortBy.
let sortedList2 = List.sortBy (fun elem -> abs elem) [1; 4; 8; -2; 5]
printfn "%A" sortedList2
Выходные данные выглядят следующим образом.
[1; -2; 4; 5; 8]
В следующем примере показано использование функции List.sortWith.В этом примере пользовательская функция сравнения compareWidgets используется сначала для сравнения значений одного поля пользовательского типа, а затем другого, если значения первого поля равны.
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
Выходные данные выглядят следующим образом.
[{ID = 92;
Rev = 1;}; {ID = 92;
Rev = 1;}; {ID = 100;
Rev = 2;}; {ID = 100;
Rev = 5;}; {ID = 110;
Rev = 1;}]
Операции поиска в списках
Списки поддерживают большое число операций поиска.Самая простая функция List.find позволяет найти первый элемент, который удовлетворяет заданному условию.
В следующем примере кода демонстрируется использование функции List.find для поиска в списке первого числа, которое делится на 5.
let isDivisibleBy number elem = elem % number = 0
let result = List.find (isDivisibleBy 5) [ 1 .. 100 ]
printfn "%d " result
Результат применения функции — 5.
Если элементы необходимо сначала преобразовать, вызовите функцию List.pick, которая принимает функцию, возвращающую значение типа option, а затем ищет первое значение типа option, равное Some(x).Вместо возврата элемента функция List.pick возвращает результат x.Если соответствующий элемент не найден, функция List.pick вызывает исключение KeyNotFoundException.В следующем коде показано использование функции 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
Выходные данные выглядят следующим образом.
"b"
Другая группа операций поиска, List.tryFind и связанные с ней функции, возвращает значение типа option.Функция List.tryFind возвращает первый элемент списка, удовлетворяющий условию, если такой элемент существует, и значение None, если элемент не существует.Версия List.tryFindIndex возвращает индекс элемента, если элемент найден, а не сам элемент.Эти функции демонстрируются в следующем коде.
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."
Выходные данные выглядят следующим образом.
The first even value is 22.
The first even value is at position 8.
Арифметические операции над списками
В модуль List встроены стандартные арифметические операции, такие как вычисление суммы или среднего.Для работы с функцией List.sum тип элемента списка должен поддерживать оператор + и нулевое значение.Этим условиям удовлетворяют все встроенные арифметические типы.Для работы с функцией List.average тип элемента должен поддерживать деление без остатка, что исключает целочисленные типы, но разрешает использование типов с плавающей запятой.Функции List.sumBy и List.averageBy принимают в качестве параметра функцию, результат которой используется для вычисления значений суммы или среднего.
В следующем коде демонстрируется использование функций List.sum, List.sumBy и 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
В результате получается 1.000000.
В следующем коде показано использование функции List.averageBy.
let avg2 = List.averageBy (fun elem -> float elem) [1 .. 10]
printfn "%f" avg2
В результате получается 5.5.
Списки и кортежи
Со списками, содержащими кортежи, можно использовать функции zip и unzip.Эти функции объединяют два списка отдельных значений в один список кортежей или разделяют один список кортежей на два списка отдельных значений.Самая простая функция List.zip принимает два списка отдельных элементов и создает один список из двухэлементных кортежей.Другая версия, List.zip3, принимает три списка отдельных элементов и создает один список из кортежей, содержащих по три элемента.В следующем примере кода показано использование функции List.zip.
let list1 = [ 1; 2; 3 ]
let list2 = [ -1; -2; -3 ]
let listZip = List.zip list1 list2
printfn "%A" listZip
Выходные данные выглядят следующим образом.
[(1, -1); (2, -2); (3; -3)]
В следующем примере кода показано использование функции List.zip3.
let list3 = [ 0; 0; 0]
let listZip3 = List.zip3 list1 list2 list3
printfn "%A" listZip3
Выходные данные выглядят следующим образом.
[(1, -1, 0); (2, -2, 0); (3, -3, 0)]
Соответствующие версии функций распаковки, List.unzip и List.unzip3, принимают списки кортежей и возвращают кортеж списков, где первый список содержит все первые элементы кортежей, второй список содержит все вторые элементы кортежей и т. д.
В следующем примере кода показано использование функции List.unzip.
let lists = List.unzip [(1,2); (3,4)]
printfn "%A" lists
printfn "%A %A" (fst lists) (snd lists)
Выходные данные выглядят следующим образом.
([1; 3], [2; 4])
[1; 3] [2; 4]
В следующем примере кода показано использование функции List.unzip3.
let listsUnzip3 = List.unzip3 [(1,2,3); (4,5,6)]
printfn "%A" listsUnzip3
Выходные данные выглядят следующим образом.
([1; 4], [2; 5], [3; 6])
Операции над элементами списков
Язык F# поддерживает разнообразные операции над элементами списков.Самая простая функция — List.iter, которая позволяет вызвать некоторую функцию для каждого элемента списка.К другим ее вариантам относятся функция List.iter2, которая позволяет выполнять операцию над элементами двух списков, функция List.iteri, которая подобна функции List.iter за тем исключением, что в качестве аргумента функции, которая вызывается для каждого элемента, передается индекс этого элемента, и функция List.iteri2, которая объединяет функции List.iter2 и List.iteri.Следующий пример кода иллюстрирует эти функции.
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
Выходные данные выглядят следующим образом.
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
Еще одна часто используемая функция, преобразующая элементы списка, это функция List.map, которая позволяет применить функцию к каждому элементу списка и поместить результаты в новый список.Функции List.map2 и List.map3 — это варианты, принимающие несколько списков.Можно также использовать функции List.mapi и List.mapi2, если, помимо элемента, функции также требуется передавать индекс каждого элемента.Единственное отличие между функциями List.mapi2 и List.mapi заключается в том, что функция List.mapi2 работает с двумя списками.В следующем примере демонстрируется использование функции List.map.
let list1 = [1; 2; 3]
let newList = List.map (fun x -> x + 1) list1
printfn "%A" newList
Выходные данные выглядят следующим образом.
[2; 3; 4]
В следующем примере демонстрируется использование функции 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
Выходные данные выглядят следующим образом.
[5; 7; 9]
В следующем примере демонстрируется использование функции List.map3.
let newList2 = List.map3 (fun x y z -> x + y + z) list1 list2 [2; 3; 4]
printfn "%A" newList2
Выходные данные выглядят следующим образом.
[7; 10; 13]
В следующем примере демонстрируется использование функции List.mapi.
let newListAddIndex = List.mapi (fun i x -> x + i) list1
printfn "%A" newListAddIndex
Выходные данные выглядят следующим образом.
[1; 3; 5]
В следующем примере демонстрируется использование функции List.mapi2.
let listAddTimesIndex = List.mapi2 (fun i x y -> (x + y) * i) list1 list2
printfn "%A" listAddTimesIndex
Выходные данные выглядят следующим образом.
[0; 7; 18]
Функция List.collect аналогична функции List.map, за исключением того, что для каждого элемента создается список, и все эти списки сцепляются в конечный список.В следующем коде для каждого элемента списка создаются три числа.Все эти числа собираются в один список.
let collectList = List.collect (fun x -> [for i in 1..3 -> x * i]) list1
printfn "%A" collectList
Выходные данные выглядят следующим образом.
[1; 2; 3; 2; 4; 6; 3; 6; 9]
Можно также использовать функцию List.filter, которая принимает логическое условие и создает новый список, содержащий только элементы, которые удовлетворяют заданному условию.
let evenOnlyList = List.filter (fun x -> x % 2 = 0) [1; 2; 3; 4; 5; 6]
В результате будет получен список [2; 4; 6].
Функция List.choose сочетает сопоставление и фильтрацию, позволяет одновременно преобразовывать и выбирать элементы.Функция List.choose применяет функцию, возвращающую значение типа option для каждого элемента списка, и возвращает новый список результатов для элементов, для которых функция возвращает значение Some.
В следующем коде демонстрируется использование функции List.choose для выбора из списка слов тех слов, которые набраны заглавными буквами.
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
Выходные данные выглядят следующим образом.
["Rome's"; "Bob's"]
Работа с несколькими списками
Списки можно объединять.Чтобы объединить два списка в один, следует использовать функцию List.append.Чтобы объединить более двух списков, следует использовать функцию 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
Операции свертки и сканирования
Некоторые операции со списками предполагают зависимости между всеми элементами списка.К операциям свертки и сканирования относятся, например, List.iter и List.map, в которых для каждого элемента вызывается некоторая функция, но эти операции содержат дополнительный параметр, называемый аккумулятором, в котором сохраняется информация на время всего вычисления.
Функция List.fold служит для выполнения вычислений со списком.
В следующем примере кода показано использование функции List.fold для выполнения различных операций.
Выполняется обход списка; аккумулятор acc представляет собой значение, передаваемое в течение всего процесса вычисления.Первый аргумент принимает аккумулятор и элемент списка, возвращая промежуточный результат вычисления для этого элемента списка.Второй аргумент — это начальное значение аккумулятора.
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])
Версии этих функций, в имена которых входит число, предназначены для работы с несколькими списками.Например, функция List.fold2 выполняет вычисления для двух списков.
Следующий пример демонстрирует использование 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 и List.scan имеют следующее различие: функция List.fold возвращает окончательное значение дополнительного параметра, а функция List.scan — список промежуточных значений дополнительного параметра (и его окончательное значение).
Каждая из этих функций включает обратную версию, например List.foldBack, которая отличается порядком перебора списка и порядком следования аргументов.Функции List.fold и List.foldBack также имеют варианты List.fold2 и List.foldBack2, принимающие два списка одинаковой длины.Функция, выполняемая для каждого из элементов, может использовать соответствующие элементы обоих списков, чтобы выполнить определенное действие.Типы элементов обоих списков могут различаться, как в приведенном ниже примере, где первый список содержит суммы транзакций по банковскому счету, а второй — типы транзакций (зачисление или списание).
// 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
Для таких вычислений, как суммирование, функции List.fold и List.foldBack дают одинаковый результат, так как он не зависит от порядка обхода.В следующем примере функция List.foldBack используется для добавления элементов в список.
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])
В следующем примере снова используется пример банковского счета.На этот раз добавляется новый тип транзакции — вычисление процентов.Теперь окончательный баланс зависит от порядка следования транзакций.
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
Функция List.reduce в определенной степени похожа на функции List.fold и List.scan, но вместо передачи отдельного аккумулятора функция List.reduce принимает некоторую функцию, которая принимает не один, а два аргумента типа элемента, и один из этих аргументов выполняет роль аккумулятора, т. е. служит для хранения промежуточного результата вычисления.Сначала функция List.reduce действует на первые два элемента списка, затем использует результат операции вместе со следующим элементом.Так как отдельный аккумулятор со своим собственным типом отсутствует, функцию List.reduce можно использовать вместо функции List.fold только в том случае, если тип аккумулятора и тип элемента совпадают.В следующем коде демонстрируется использование функции List.reduce.Если предоставленный список не содержит элементов, функция List.reduce вызывает исключение.
В следующем коде первому вызову лямбда-выражения передаются аргументы 2 и 4, а возвращается число 6; следующему вызову передаются аргументы 6 и 10, поэтому в результате получается 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
Преобразование между списками и коллекциями других типов
Модуль List предоставляет функции для преобразования в последовательности и массивы и обратного преобразования.Чтобы выполнить преобразование в последовательность или из последовательности, используйте функцию List.toSeq или List.ofSeq.Чтобы выполнить преобразование в массив или из массива, используйте функцию List.toArray или List.ofArray.
Дополнительные операции
Сведения о дополнительных операциях со списками см. в разделе справочной библиотеки Модуль Collections.List (F#).