Поделиться через


Списки (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 ]

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

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

Для выполнения операций со списками используйте функции в модуле списка.

Поскольку списки в языке F# являются неизменными, то операции изменения не изменяют существующие списки, а создают новые.

Списки в языке F# реализуются как однократно связанные списки, т. е. операции, которые обращаются только к началу списка, выглядят как O(1), а операции, которые обращаются к элементам, — как O(n).

Свойства

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

Свойство

Тип

Описание

Заголовок

'T

Первый элемент

Empty

'T list

Статическое свойство, которое возвращает пустой список соответствующего типа.

IsEmpty

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

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

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

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

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

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

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

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]

Функции модуля

Модуль списка предоставляет функции для доступа к элементам списка. Самым легким и быстрым для доступа является первоначальный элемент. Используется свойство 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, который использует функцию, возвращающую параметр, и ищет первое значение параметра 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, и связанные с ней функции возвращают значение параметра. Функция 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.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.

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

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

См. также

Ссылки

Последовательности (F#)

Параметры (F#)

Другие ресурсы

Справочник по языку F#

Типы языка F#

Массивы (F#)