Списки

В языке F# список — это упорядоченная, неизменная серия элементов одного типа. Чтобы выполнить основные операции со списками, используйте функции в модуле List.

Создание и инициализация списков

Список можно определить путем прямого перечисления элементов, разделенных точкой с запятой и заключенных в квадратные скобки, как показано в следующей строке кода.

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 ]

Пустой список определяется парой квадратных скобок, между которыми ничего не указано.

// An empty list.
let listEmpty = []

Также список можно создать с помощью выражения последовательности. Дополнительные сведения см . в разделе "Выражения последовательности". Например, в следующем коде создается список квадратов целочисленных значений от 1 до 10.

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

Операторы для работы со списками

Оператор :: позволяет добавлять элементы в список. Если список 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).

Свойства

Тип списка поддерживает следующие свойства.

Свойство Type Описание
Head 'T Первый элемент
Пусто 'T list Статическое свойство, которое возвращает пустой список соответствующего типа.
IsEmpty bool true Значение , если в списке нет элементов.
Элемент 'T Элемент с указанным индексом (начинается с нуля).
Длина int Число элементов.
Tail '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))

Использование списков

Программирование с использованием списков позволяет выполнять сложные операции с небольшим количеством кода. В данном разделе описываются операции со списками, важные для функционального программирования.

Рекурсия со списками

Списки однозначно подходят для техник рекурсивного программирования. Рассмотрим операцию, в которой должен участвовать каждый элемент списка. Это можно сделать рекурсивно, т. е. сначала обработать начало списка, затем перейти к хвосту — более короткому списку, состоящему из первоначального списка без первого элемента, а потом снова перейти на следующий уровень рекурсии.

Для написания такой рекурсивной функции используется оператор (::) в сопоставлении шаблонов, который позволяет отделить начало списка от хвоста.

Следующий пример кода показывает, как использовать сопоставление шаблонов для реализации рекурсивной функции, выполняющей операции над списком.

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

Предыдущий код хорошо работает для небольших списков, но при работе со списками большого размера может случиться переполнение стека. Следующий код улучшает предыдущий за счет использования аргумента аккумулирования — это стандартная техника работы с рекурсивными функциями. Использование аргумента аккумулирования делает функцию рекурсивной по отношению к хвосту, что экономит место в стеке.

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 или module function 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 использует универсальное сравнение по умолчанию. Общее сравнение выполняется с помощью глобальных операторов на основе функции общего сравнения значений. Оно эффективно работает с различными типами элементов, такими как числовые типы, кортежи, записи, размеченные объединения, списки, массивы и любой другой тип, включающий System.IComparable. Для типов, включающих System.IComparable, общее сравнение выполняется с помощью функции System.IComparable.CompareTo(). Общее сравнение также работает со строками, но использует культурно-независимый порядок сортировки. Общее сравнение не следует применять к неподдерживаемым типам, например типам функций. К тому же выполнение общего сравнения по умолчанию лучше всего подходит для слабо структурированных типов. Для сильно структурированных типов, которые необходимо часто сравнивать и сортировать, можно использовать функцию System.IComparable и метод System.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, который принимает функцию, которая возвращает параметр, и ищет первое значение параметра, которое является Some(x). Вместо возвращения элемента функция List.pick возвращает результат x. Если совпадения не найдены, функция List.pick возвращает System.Collections.Generic.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 и связанные функции, возвращает значение параметра. Функция 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 должен поддерживать + оператор и иметь нулевое значение. Все встроенные арифметические типы удовлетворяют этим условиям. Для работы с 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.

Списки и кортежи

Для работы со списками, содержащими кортежи, можно использовать функции упаковки и распаковки. Они объединяют два списка с одним значением в один список кортежей или разбивают один список кортежей на два списка с одним значением. Простейшая функция 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)]

Соответствующие версии unzip, 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 работает с двумя списками. В следующем примере показан Список.карта.

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.select позволяет одновременно преобразовывать и выбирать элементы. List.choose Применяет функцию, возвращающую параметр к каждому элементу списка, и возвращает новый список результатов для элементов, когда функция возвращает значение 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 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])

В следующем примере снова используется банковский счет. В этот раз добавляется новый тип транзакций: расчет процентов. Конечный баланс теперь зависит от порядка транзакций.

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.scanList.fold и, за исключением того, что вместо передачи вокруг отдельного накопительного элемента 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.

Дополнительные операции

Дополнительные сведения о дополнительных операциях со списками см. в разделе " Модуль списка списков библиотеки".

См. также