Массивы (F#)
Массивы представляют собой фиксированный размер, отсчитываемые от нуля коллекции последовательных элементов данных, которые являются одинаковыми типами.
Создание массивов
Массивы можно создавать несколькими способами. Можно создать небольшой массив, перечислив последовательные значения между [|
и |]
разделенными точкой с запятой, как показано в следующих примерах.
let array1 = [| 1; 2; 3 |]
Вы также можете поместить каждый элемент в отдельную строку, в этом случае разделитель с запятой является необязательным.
let array1 =
[|
1
2
3
|]
Тип элементов массива выводится из используемых литералах и должен быть согласован.
// This is an array of 3 integers.
let array1 = [| 1; 2; 3 |]
// This is an array of a tuple of 3 integers.
let array2 = [| 1, 2, 3 |]
Следующий код вызывает ошибку, так как 3.0 является плавающей и 1 и 2 целыми числами.
// Causes an error. The 3.0 (float) cannot be converted to integer implicitly.
// let array3 = [| 1; 2; 3.0 |]
Следующий код также вызывает ошибку, так как 1,2
является кортежем и 3
является целым числом.
// Causes an error too. The 3 (integer) cannot be converted to tuple implicitly.
// let array4 = [| 1, 2; 3 |]
Для создания массивов можно также использовать выражения последовательности. Ниже приведен пример, который создает массив квадратов целых чисел от 1 до 10.
let array3 = [| for i in 1 .. 10 -> i * i |]
Чтобы создать массив, в котором все элементы инициализированы до нуля, используйте Array.zeroCreate
.
let arrayOfTenZeroes : int array = Array.zeroCreate 10
Элементы Access
Доступ к элементам массива можно получить с помощью квадратных скобок ([
и ]
). Исходный синтаксис точек (.[index]
) по-прежнему поддерживается, но больше не рекомендуется по состоянию на F# 6.0.
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# — это тип System.Arrayплатформа .NET Framework. Поэтому массивы F# поддерживают все функциональные возможности, доступные в System.Array.
Модуль Array
поддерживает операции с одномерными массивами. Модули и Array4D
содержат функции, поддерживающие операции с массивами Array2D
Array3D
двух, трех и четырех измерений соответственно. Вы можете создать массивы ранга, превышающие четыре, с помощью System.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
операция с System.Text.StringBuilder типом влияет на базовый System.Text.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
показано. Обратите внимание, что тип элемента массива не должен соответствовать типу значения, возвращаемого в типе параметра. В этом примере тип элемента является int
результатом полиномиальной функции в 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
показано.
Array.concat [ [|0..3|] ; [|4|] ]
//output [|0; 1; 2; 3; 4|]
Array.concat [| [|0..3|] ; [|4|] |]
//output [|0; 1; 2; 3; 4|]
Array.filter
принимает логическое условие и создает новый массив, содержащий только те элементы из входного массива, для которого условие имеет значение true. В следующем коде 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!"
Вы можете легко объединить функции в модуле массива, который преобразует массивы с помощью оператора конвейера (|>
), как показано в следующем примере.
[| 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
функцию, которая также доступна для массивов до четырех измерений. В следующем примере кода сначала показано, как создать массив массивов, содержащих нужные элементы, а затем использовать 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>[,,]
и т. д. для массивов более высоких измерений.
Для многомерных массивов также доступно только подмножество функций, доступных для одномерных массивов.
Срезы массивов и многомерные массивы
В двухмерном массиве (матрице) можно извлечь вложенную матрицу, указав диапазоны и используя дикий символ карта (*
) для указания целых строк или столбцов.
// Get rows 1 to N from an NxM matrix (returns a matrix):
matrix[1.., *]
// Get rows 1 to 3 from a matrix (returns a matrix):
matrix[1..3, *]
// Get columns 1 to 3 from a matrix (returns a matrix):
matrix[*, 1..3]
// Get a 3x3 submatrix:
matrix[1..3, 1..3]
Вы можете разложить многомерный массив на вложенные массивы одного или нижнего измерения. Например, можно получить вектор из матрицы, указав одну строку или столбец.
// Get row 3 from a matrix as a vector:
matrix[3, *]
// Get column 3 from a matrix as a vector:
matrix[*, 3]
Этот синтаксис срезов можно использовать для типов, реализующих операторы доступа к элементам и перегруженные GetSlice
методы. Например, следующий код создает тип матрицы, который упаковывает массив F# 2D, реализует свойство Item для обеспечения поддержки индексирования массива GetSlice
и реализует три версии. Если этот код можно использовать в качестве шаблона для типов матриц, можно использовать все операции срезов, описываемые в этом разделе.
type Matrix<'T>(N: int, M: int) =
let internalArray = Array2D.zeroCreate<'T> N M
member this.Item
with get(a: int, b: int) = internalArray[a, b]
and set(a: int, b: int) (value:'T) = internalArray[a, b] <- value
member this.GetSlice(rowStart: int option, rowFinish : int option, colStart: int option, colFinish : int option) =
let rowStart =
match rowStart with
| Some(v) -> v
| None -> 0
let rowFinish =
match rowFinish with
| Some(v) -> v
| None -> internalArray.GetLength(0) - 1
let colStart =
match colStart with
| Some(v) -> v
| None -> 0
let colFinish =
match colFinish with
| Some(v) -> v
| None -> internalArray.GetLength(1) - 1
internalArray[rowStart..rowFinish, colStart..colFinish]
member this.GetSlice(row: int, colStart: int option, colFinish: int option) =
let colStart =
match colStart with
| Some(v) -> v
| None -> 0
let colFinish =
match colFinish with
| Some(v) -> v
| None -> internalArray.GetLength(1) - 1
internalArray[row, colStart..colFinish]
member this.GetSlice(rowStart: int option, rowFinish: int option, col: int) =
let rowStart =
match rowStart with
| Some(v) -> v
| None -> 0
let rowFinish =
match rowFinish with
| Some(v) -> v
| None -> internalArray.GetLength(0) - 1
internalArray[rowStart..rowFinish, col]
module test =
let generateTestMatrix x y =
let matrix = new Matrix<float>(3, 3)
for i in 0..2 do
for j in 0..2 do
matrix[i, j] <- float(i) * x - float(j) * y
matrix
let test1 = generateTestMatrix 2.3 1.1
let submatrix = test1[0..1, 0..1]
printfn $"{submatrix}"
let firstRow = test1[0,*]
let secondRow = test1[1,*]
let firstCol = test1[*,0]
printfn $"{firstCol}"
Логические функции в массивах
Функции 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
, или вызывает System.Collections.Generic.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
, за исключением того, что результат является типом параметра, и возвращается None
, если элемент не найден. Array.tryFind
следует использовать вместо того, Array.find
когда вы не знаете, находится ли соответствующий элемент в массиве. Точно так же, Array.tryFindIndex
как Array.findIndex
и тип параметра, является возвращаемым значением. Если элемент не найден, параметр имеет значение 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
Failed to find a matching element.
Используйте 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.
Did not find an element that is both a perfect square and a perfect cube.
Выполнение вычислений на массивах
Функция 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. Примеры использования см. в разделе "Списки".
Изменение массивов
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.