Массивы (F#)
Массивы — это начинающиеся с нулевого индекса изменяемые коллекции фиксированного размера, содержащие последовательные элементы данных одного типа.
Создание массивов
Массивы можно создавать несколькими способами.Небольшой массив можно создать, последовательно перечислив разделяемые точкой с запятой значения между символами [| и |], как показано в следующих примерах.
let array1 = [| 1; 2; 3 |]
Также можно указать каждый элемент в отдельной строке (в этом случае точку с запятой можно опустить).
let array1 =
[|
1
2
3
|]
Тип элементов массива определяется по указанным литералам, и он должен быть одинаковым для всех элементов.Следующий код вызывает ошибку, поскольку 1.0 — это число с плавающей запятой, а 2 и 3 — целые числа.
// Causes an error.
// let array2 = [| 1.0; 2; 3 |]
Для создания массивов можно также использовать выражения последовательностей.Ниже приведен пример, в котором создается массив квадратов целых чисел от 1 до 10.
let array3 = [| for i in 1 .. 10 -> i * i |]
Чтобы создать массив нулевых элементов, используйте функцию Array.zeroCreate.
let arrayOfTenZeroes : int array = Array.zeroCreate 10
Доступ к элементам
Для доступа к элементам массива используется оператор точки (.) и квадратные скобки ([ и ]).
array1.[0]
Индексы массивов начинаются с 0.
Для доступа к элементам массива также можно использовать диапазонное представление, позволяющее указать поддиапазон массива.Ниже приведены примеры диапазонного представления.
// Accesses elements from 0 to 2.
array1.[0..2]
// Accesses elements from the beginning of the array to 2.
array1.[..2]
// Accesses elements from 2 to the end of the array.
array1.[2..]
При использовании диапазонного представления создается новая копия массива.
Типы массивов и модули
Тип всех массивов в языке F# — это тип платформы .NET Framework Array.Поэтому массивы в языке F# поддерживают все функциональные возможности класса Array.
Модуль библиотеки Microsoft.FSharp.Collections.Array поддерживает операции над одномерными массивами.Модули Array2D, Array3D и Array4D содержат функции, поддерживающие операции над двумерными, трехмерными и четырехмерными массивами соответственно.Массивы с размерностью более 4 можно создавать с помощью класса Array.
Простые функции
Функция Array.get получает элемент.Функция Array.length возвращает длину массива.Функция Array.set задает для элемента указанное значение.Следующий пример кода иллюстрирует использование этих функций.
let array1 = Array.create 10 ""
for i in 0 .. array1.Length - 1 do
Array.set array1 i (i.ToString())
for i in 0 .. array1.Length - 1 do
printf "%s " (Array.get array1 i)
Выходные данные выглядят следующим образом.
0 1 2 3 4 5 6 7 8 9
Функции, создающие массивы
Несколько функций создают массивы, не требуя наличия существующего массива.Функция Array.empty создает новый массив, не содержащий никаких элементов.Функция Array.create создает массив указанного размера и задает для всех элементов указанные значения.Функция Array.init создает массив заданного размера, используя функцию для создания элементов.Функция Array.zeroCreate создает массив, всем элементам которого присваивается нулевое значение, соответствующее типу массива.Следующий код демонстрирует эти функции.
let myEmptyArray = Array.empty
printfn "Length of empty array: %d" myEmptyArray.Length
printfn "Array of floats set to 5.0: %A" (Array.create 10 5.0)
printfn "Array of squares: %A" (Array.init 10 (fun index -> index * index))
let (myZeroArray : float array) = Array.zeroCreate 10
Выходные данные выглядят следующим образом.
Length of empty array: 0
Area of floats set to 5.0: [|5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0|]
Array of squares: [|0; 1; 4; 9; 16; 25; 36; 49; 64; 81|]
Функция Array.copy создает новый массив, содержащий элементы, скопированные из существующего массива.Обратите внимание, что копия является неполной, т. е., если элемент имеет ссылочный тип, копируется только ссылка, а объект, на который указывает ссылка, не копируется.Это показано в следующем примере кода.
open System.Text
let firstArray : StringBuilder array = Array.init 3 (fun index -> new StringBuilder(""))
let secondArray = Array.copy firstArray
// Reset an element of the first array to a new value.
firstArray.[0] <- new StringBuilder("Test1")
// Change an element of the first array.
firstArray.[1].Insert(0, "Test2") |> ignore
printfn "%A" firstArray
printfn "%A" secondArray
Результат выполнения предыдущего кода:
[|Test1; Test2; |]
[|; Test2; |]
Строка Test1 содержится только в первом массиве, так как операция создания нового элемента перезаписывает ссылку в массиве firstArray, но не влияет на исходную ссылку на пустую строку, которая по-прежнему остается в массиве secondArray.Строка Test2 отображается в обоих массивах, так как операция Insert для типа StringBuilder действует на исходный объект StringBuilder, ссылка на который содержится в обоих массивах.
Функция Array.sub создает новый массив из ограниченного диапазона элементов другого массива.Поддиапазон задается указанием начального индекса и длины.В следующем коде демонстрируется использование функции Array.sub.
let a1 = [| 0 .. 99 |]
let a2 = Array.sub a1 5 10
printfn "%A" a2
Выходные данные показывают, что подмассив начинается с элемента 5 и содержит 10 элементов.
[|5; 6; 7; 8; 9; 10; 11; 12; 13; 14|]
Функция Array.append создает новый массив, объединяя два существующих массива.
Следующий код демонстрирует использование функции Array.append.
printfn "%A" (Array.append [| 1; 2; 3|] [| 4; 5; 6|])
Выполнение предыдущего кода дает следующий результат.
[|1; 2; 3; 4; 5; 6|]
Функция Array.choose выбирает элементы массива, включаемые в новый массив.Следующий код демонстрирует использование функции Array.choose.Обратите внимание, что тип элементов массива на обязательно должен соответствовать типу значения, возвращаемому в типе option.В данном примере элемент имеет тип int, а значение типа option является результатом выполнения полиномиальной функции elem*elem - 1 и является числом с плавающей запятой.
printfn "%A" (Array.choose (fun elem -> if elem % 2 = 0 then
Some(float (elem*elem - 1))
else
None) [| 1 .. 10 |])
Выполнение предыдущего кода дает следующий результат.
[|3.0; 15.0; 35.0; 63.0; 99.0|]
Функция Array.collect применяет указанную функцию к каждому элементу существующего массива, а затем собирает элементы, созданные этой функцией, и объединяет их в новый массив.Следующий код демонстрирует использование функции Array.collect.
printfn "%A" (Array.collect (fun elem -> [| 0 .. elem |]) [| 1; 5; 10|])
Выполнение предыдущего кода дает следующий результат.
[|0; 1; 0; 1; 2; 3; 4; 5; 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
Функция Array.concat принимает последовательность массивов и объединяет их в один массив.Следующий код демонстрирует использование функции Array.concat.
let multiplicationTable max = seq { for i in 1 .. max -> [| for j in 1 .. max -> (i, j, i*j) |] }
printfn "%A" (Array.concat (multiplicationTable 3))
Выполнение предыдущего кода дает следующий результат.
[|(1, 1, 1); (1, 2, 2); (1, 3, 3); (2, 1, 2); (2, 2, 4); (2, 3, 6); (3, 1, 3);
(3, 2, 6); (3, 3, 9)|]
Функция Array.filter принимает функцию логического условия и создает новый массив, содержащий только те элементы входного массива, для которых логическое условие истинно.Следующий код демонстрирует использование функции Array.filter.
printfn "%A" (Array.filter (fun elem -> elem % 2 = 0) [| 1 .. 10|])
Выполнение предыдущего кода дает следующий результат.
[|2; 4; 6; 8; 10|]
Функция Array.rev создает новый массив, изменяя порядок элементов существующего массива на обратный.Следующий код демонстрирует использование функции Array.rev.
let stringReverse (s: string) =
System.String(Array.rev (s.ToCharArray()))
printfn "%A" (stringReverse("!dlrow olleH"))
Выполнение предыдущего кода дает следующий результат.
"Hello world!"
Функции модуля Array, преобразующие массивы, можно легко комбинировать с помощью оператора конвейера (|>), как показано в следующем примере.
[| 1 .. 10 |]
|> Array.filter (fun elem -> elem % 2 = 0)
|> Array.choose (fun elem -> if (elem <> 8) then Some(elem*elem) else None)
|> Array.rev
|> printfn "%A"
Результат получается следующим:
[|100; 36; 16; 4|]
Многомерные массивы
Существует возможность создания многомерных массивов, но синтаксис литералов таких массивов не определен.Для создания массива из последовательности последовательностей элементов массива используйте оператор array2D.Последовательности могут быть литералами массивов или списков.Например, следующий код создает двумерный массив.
let my2DArray = array2D [ [ 1; 0]; [0; 1] ]
Для инициализации двумерного массива можно также использовать функцию Array2D.init; аналогичные функции предусмотрены для трех- и четырехмерных массивов.Эти функции принимают в качестве параметра функцию, используемую для создания элементов.Чтобы создать двумерный массив, содержащий элементы, для которых вместо функции заданы исходные значения, используйте функцию Array2D.create, которая также доступна для массивов размерностью вплоть до 4.В следующем примере кода показано создание массива массивов с нужными элементами, а затем создание нужного двумерного массива с помощью функции Array2D.init.
let arrayOfArrays = [| [| 1.0; 0.0 |]; [|0.0; 1.0 |] |]
let twoDimensionalArray = Array2D.init 2 2 (fun i j -> arrayOfArrays.[i].[j])
Синтаксис индексирования и указания диапазонов массивов поддерживается для массивов не выше 4 ранга.При указании индекса многомерного массива различные составляющие индекса разделяются запятой, как показано в следующем примере кода.
twoDimensionalArray.[0, 1] <- 1.0
Тип двумерного массива обозначается как <type>[,] (например,int[,], double[,]), тип трехмерного массива — как <type>[,,] и так далее для многомерных массивов.
Для многомерных массивов доступна только часть функций, предусмотренных для одномерных массивов.Дополнительные сведения см. в разделах Модуль Collections.Array (F#), Модуль Collections.Array2D (F#), Модуль Collections.Array3D (F#) и Модуль Collections.Array4D (F#).
Логические функции для массивов
Функции Array.exists и Array.exists2 проверяют элементы в одном и в двух массивах соответственно.Эти функции принимают функцию проверки и возвращают значение true, если любой из элементов (или любая из пар элементов в случае функции Array.exists2) отвечает условию.
В следующем коде демонстрируется использование функций Array.exists и Array.exists2.В этих примерах новые функции создаются путем использования только одного из аргументов, в данных случаях — аргумента функции.
let allNegative = Array.exists (fun elem -> abs (elem) = elem) >> not
printfn "%A" (allNegative [| -1; -2; -3 |])
printfn "%A" (allNegative [| -10; -1; 5 |])
printfn "%A" (allNegative [| 0 |])
let haveEqualElement = Array.exists2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (haveEqualElement [| 1; 2; 3 |] [| 3; 2; 1|])
Выполнение предыдущего кода дает следующий результат.
true
false
false
true
Аналогичным образом функция Array.forall проверяет, все ли элементы массива удовлетворяют заданному логическому условию.Вариант функции Array.forall2 выполняет такую же проверку с помощью логической функции, принимающей элементы двух массивов одинаковой длины.Следующий код иллюстрирует использование этих функций.
let allPositive = Array.forall (fun elem -> elem > 0)
printfn "%A" (allPositive [| 0; 1; 2; 3 |])
printfn "%A" (allPositive [| 1; 2; 3 |])
let allEqual = Array.forall2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (allEqual [| 1; 2 |] [| 1; 2 |])
printfn "%A" (allEqual [| 1; 2 |] [| 2; 1 |])
Результат выполнения этих примеров выглядит следующим образом.
false
true
true
false
Поиск в массивах
Функция Array.find принимает логическую функцию и возвращает первый элемент, для которого функция возвращает значение true или вызывает исключение KeyNotFoundException, если элемент, удовлетворяющий условию, не найден.Функция Array.findIndex аналогична функции Array.find, но возвращает индекс элемента, а не сам элемент.
В следующем коде функции Array.find и Array.findIndex используются для поиска числа, которое одновременно является точным квадратом и точным кубом.
let arrayA = [| 2 .. 100 |]
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
let element = Array.find (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
let index = Array.findIndex (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
printfn "The first element that is both a square and a cube is %d and its index is %d." element index
Выходные данные выглядят следующим образом.
The first element that is both a square and a cube is 64 and its index is 62.
Функция Array.tryFind аналогична функции Array.find, но ее результат имеет тип option, а если элемент не найден, она возвращает значение None.Функция Array.tryFind должна использоваться вместо функции Array.find, если не известно, имеется ли в массиве требуемый элемент.Аналогичным образом функция Array.tryFindIndex подобна функции Array.findIndex, но в качестве результата возвращает значение типа option.Если элемент не найден, тип option имеет значение None.
В следующем коде демонстрируется использование функции Array.tryFind.Этот код зависит от предыдущего кода.
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
let lookForCubeAndSquare array1 =
let result = Array.tryFind (fun elem -> isPerfectSquare elem && isPerfectCube elem) array1
match result with
| Some x -> printfn "Found an element: %d" x
| None -> printfn "Failed to find a matching element."
lookForCubeAndSquare [| 1 .. 10 |]
lookForCubeAndSquare [| 100 .. 1000 |]
lookForCubeAndSquare [| 2 .. 50 |]
Выходные данные выглядят следующим образом.
Found an element: 1
Found an element: 729
Используйте функцию Array.tryPick, если требуется не только найти элемент, но и преобразовать его.Ее результат — первый элемент, для которого функция возвращает преобразованный элемент как значение параметра, или значение None, если такой элемент не найден.
В следующем коде показано использование функции Array.tryPick.В этом случае для упрощения кода вместо лямбда-выражения используются несколько локальных вспомогательных функций.
let findPerfectSquareAndCube array1 =
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
// intFunction : (float -> float) -> int -> int
// Allows the use of a floating point function with integers.
let intFunction function1 number = int (round (function1 (float number)))
let cubeRoot x = System.Math.Pow(x, 1.0/3.0)
// testElement: int -> (int * int * int) option
// Test an element to see whether it is a perfect square and a perfect
// cube, and, if so, return the element, square root, and cube root
// as an option value. Otherwise, return None.
let testElement elem =
if isPerfectSquare elem && isPerfectCube elem then
Some(elem, intFunction sqrt elem, intFunction cubeRoot elem)
else None
match Array.tryPick testElement array1 with
| Some (n, sqrt, cuberoot) -> printfn "Found an element %d with square root %d and cube root %d." n sqrt cuberoot
| None -> printfn "Did not find an element that is both a perfect square and a perfect cube."
findPerfectSquareAndCube [| 1 .. 10 |]
findPerfectSquareAndCube [| 2 .. 100 |]
findPerfectSquareAndCube [| 100 .. 1000 |]
findPerfectSquareAndCube [| 1000 .. 10000 |]
findPerfectSquareAndCube [| 2 .. 50 |]
Выходные данные выглядят следующим образом.
Found an element 1 with square root 1 and cube root 1.
Found an element 64 with square root 8 and cube root 4.
Found an element 729 with square root 27 and cube root 9.
Found an element 4096 with square root 64 and cube root 16.
Вычисления с массивами
Функция Array.average возвращает среднее значение каждого элемента в массиве.Она ограничена типами элементов, поддерживающими точное деление на целое число, что включает типы с плавающей запятой, но не целочисленные типы.Функция Array.averageBy возвращает среднее значение результатов вызова функции для каждого элемента.Для массивов целого типа можно использовать функцию Array.averageBy и с помощью функции преобразовать каждый элемент массива в число с плавающей запятой для вычисления.
С помощью функции Array.max или Array.min можно получить максимальный и минимальный элемент, если тип элементов поддерживает такую возможность.Аналогичным образом функции Array.maxBy и Array.minBy позволяют сначала выполнить функцию, например для преобразования в тип, поддерживающий сравнение.
Функция Array.sum складывает элементы массива, а функция Array.sumBy вызывает вычислительную функцию для каждого элемента и складывает результаты.
Чтобы выполнить функцию для каждого элемента массива без сохранения возвращаемого значения, используйте функцию Array.iter.Для функции, выполняющей операции над двумя массивами одинаковой длины, используйте функцию Array.iter2.Если также требуется сохранить результаты выполнения функции в массиве, используйте функцию Array.map или Array.map2, которая работает одновременно с двумя массивами.
Модифицированные функции Array.iteri и Array.iteri2 позволяют использовать индекс элемента при вычислении, как и функции Array.mapi и Array.mapi2.
Функции Array.fold, Array.foldBack, Array.reduce, Array.reduceBack, Array.scan и Array.scanBack выполняют алгоритмы, работающие со всеми элементами массива.Аналогичным образом функции Array.fold2 и Array.foldBack2 выполняют вычислительные операции над двумя массивами.
Эти функции для выполнения вычислений соответствуют функциям с такими же именами из модуля List.Примеры использования см. в разделе Списки (F#).
Изменение массивов
Функция Array.set задает для элемента указанное значение.Функция Array.fill задает указанное значение диапазону элементов массива.В следующем примере кода приведен пример функции Array.fill.
let arrayFill1 = [| 1 .. 25 |]
Array.fill arrayFill1 2 20 0
printfn "%A" arrayFill1
Выходные данные выглядят следующим образом.
[|1; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 23; 24; 25|]
С помощью функции Array.blit можно скопировать часть массива в другой массив.
Преобразование между различными типами
Функция Array.ofList создает новый массив из списка.Функция Array.ofSeq создает новый массив из последовательности.Функции Array.toList и Array.toSeq выполняют преобразование типа массива в типы этих других коллекций.
Сортировка массивов
Функция Array.sort позволяет упорядочить массив с помощью универсальной функции сравнения.Функция Array.sortBy позволяет указать функцию, создающую значение (называемое ключом), по которому выполняется упорядочение с помощью универсальной функции сравнения.Функция Array.sortWith позволяет использовать пользовательскую функцию сравнения.Функции Array.sort, Array.sortBy и Array.sortWith возвращают упорядоченный массив в виде нового массива.Варианты функций Array.sortInPlace, Array.sortInPlaceBy и Array.sortInPlaceWith не возвращают новый массив, а изменяют существующий.
Массивы и кортежи
Функции Array.zip и Array.unzip преобразуют массивы пар кортежей в кортежи массивов и наоборот.Функции Array.zip3 и Array.unzip3 действуют аналогичным образом, но для кортежей из трех элементов или трех массивов.
Параллельные вычисления с массивами
Модуль Array.Parallel содержит функции для выполнения параллельных вычислений с массивами.Этот модуль не доступен для приложений, предназначенных для платформы .NET Framework версии ниже 4.