Tableaux (F#)
Les tableaux sont des collections de taille fixe, de base zéro et mutables d'éléments de données consécutifs qui sont tous du même type.
Création de tableaux
Vous pouvez créer des tableaux de plusieurs façons. Vous pouvez créer un petit tableau en répertoriant des valeurs consécutives entre [| et |] et en les séparant par un point-virgule, comme indiqué dans les exemples suivants.
let array1 = [| 1; 2; 3 |]
Vous pouvez également placer chaque élément sur une ligne distincte, auquel cas le point-virgule de séparation est facultatif.
let array1 =
[|
1
2
3
|]
Le type des éléments du tableau est déduit des littéraux utilisés et doit être cohérent. Le code suivant provoque une erreur, car 1.0 est un float alors que 2 et 3 sont des entiers.
// Causes an error.
// let array2 = [| 1.0; 2; 3 |]
Vous pouvez également utiliser des expressions de séquence pour créer des tableaux. Voici un exemple qui crée un tableau de carrés d'entiers de 1 à 10.
let array3 = [| for i in 1 .. 10 -> i * i |]
Pour créer un tableau dans lequel tous les éléments sont initialisés à zéro, utilisez Array.zeroCreate.
let arrayOfTenZeroes : int array = Array.zeroCreate 10
Accès aux éléments
Vous pouvez accéder aux éléments de tableau à l'aide d'un opérateur point (.) et de crochets ([ et ]).
array1.[0]
Les index de tableau commencent à 0.
Vous pouvez également accéder aux éléments de tableau à l'aide de la notation de découpage, qui vous permet de spécifier une sous-plage du tableau. Voici quelques exemples de notation de découpage.
// 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..]
Lorsque la notation de découpage est utilisée, une nouvelle copie du tableau est créée.
Types de tableau et modules
Tous les tableaux F# sont du type .NET Framework Array. Par conséquent, les tableaux F# prennent en charge toutes les fonctionnalités disponibles dans Array.
Le module de bibliothèque Microsoft.FSharp.Collections.Array prend en charge les opérations sur les tableaux unidimensionnels. Les modules Array2D, Array3D et Array4D contiennent les fonctions qui prennent en charge les opérations sur les tableaux à deux, trois et quatre dimensions, respectivement. Vous pouvez créer des tableaux de rang supérieur à quatre à l'aide de Array.
Fonctions simples
Array.get obtient un élément. Array.length donne la longueur d'un tableau. Array.set affecte à un élément une valeur spécifiée. L'exemple de code suivant illustre l'utilisation de ces fonctions.
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)
La sortie est la suivante.
0 1 2 3 4 5 6 7 8 9
Fonctions qui créent des tableaux
Plusieurs fonctions créent des tableaux sans nécessiter de tableau existant. Array.empty crée un tableau qui ne contient pas d'éléments. Array.create crée un tableau d'une taille spécifiée et affecte à tous les éléments les valeurs fournies. Array.init crée un tableau à partir d'une dimension et d'une fonction pour générer les éléments. Array.zeroCreate crée un tableau dans lequel tous les éléments sont initialisés à la valeur zéro pour le type du tableau. Le code suivant illustre ces fonctions.
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
La sortie est la suivante.
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 crée un tableau qui contient des éléments copiés d'un tableau existant. Notez que la copie est une copie superficielle, ce qui signifie que si le type d'élément est un type référence, seule la référence est copiée, pas l'objet sous-jacent. L'exemple de code suivant illustre ceci.
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
Le résultat du code ci-dessus est le suivant :
[|Test1; Test2; |]
[|; Test2; |]
La chaîne Test1 s'affiche uniquement dans le premier tableau parce que l'opération de création d'un élément remplace la référence dans firstArray, mais n'affecte pas la référence d'origine à une chaîne vide qui est encore présente dans secondArray. La chaîne Test2 s'affiche dans les deux tableaux parce que l'opération Insert sur le type StringBuilder affecte l'objet StringBuilder sous-jacent, référencé dans les deux tableaux.
Array.sub génère un nouveau tableau à partir d'une sous-plage d'un tableau. Vous spécifiez la sous-plage en fournissant l'index de départ et la longueur. Le code suivant illustre l'utilisation d'Array.sub.
let a1 = [| 0 .. 99 |]
let a2 = Array.sub a1 5 10
printfn "%A" a2
La sortie montre que le sous-tableau démarre à l'élément 5 et contient 10 éléments.
[|5; 6; 7; 8; 9; 10; 11; 12; 13; 14|]
Array.append crée un tableau en combinant deux tableaux existants.
Le code suivant illustre Array.append.
printfn "%A" (Array.append [| 1; 2; 3|] [| 4; 5; 6|])
Le résultat du code précédent est comme suit.
[|1; 2; 3; 4; 5; 6|]
Array.choose sélectionne des éléments d'un tableau à inclure dans un nouveau tableau. Le code suivant illustre Array.choose. Notez que le type d'élément du tableau n'a pas à correspondre au type de la valeur retournée dans le type d'option. Dans cet exemple, le type d'élément est int et l'option est le résultat d'une fonction polynomiale, elem*elem - 1, comme un nombre à virgule flottante.
printfn "%A" (Array.choose (fun elem -> if elem % 2 = 0 then
Some(float (elem*elem - 1))
else
None) [| 1 .. 10 |])
Le résultat du code précédent est comme suit.
[|3.0; 15.0; 35.0; 63.0; 99.0|]
Array.collect exécute une fonction spécifiée sur chaque élément de tableau d'un tableau existant, puis collecte les éléments générés par la fonction et les combine dans un nouveau tableau. Le code suivant illustre Array.collect.
printfn "%A" (Array.collect (fun elem -> [| 0 .. elem |]) [| 1; 5; 10|])
Le résultat du code précédent est comme suit.
[|0; 1; 0; 1; 2; 3; 4; 5; 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
Array.concat prend une séquence de tableaux et les combine dans un tableau unique. Le code suivant illustre 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))
Le résultat du code précédent est comme suit.
[|(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 prend une fonction de condition booléenne et génère un nouveau tableau contenant uniquement les éléments du tableau d'entrée pour lesquels la condition est remplie. Le code suivant illustre Array.filter.
printfn "%A" (Array.filter (fun elem -> elem % 2 = 0) [| 1 .. 10|])
Le résultat du code précédent est comme suit.
[|2; 4; 6; 8; 10|]
Array.rev génère un nouveau tableau en inversant l'ordre d'un tableau existant. Le code suivant illustre Array.rev.
let stringReverse (s: string) =
System.String(Array.rev (s.ToCharArray()))
printfn "%A" (stringReverse("!dlrow olleH"))
Le résultat du code précédent est comme suit.
"Hello world!"
Vous pouvez combiner facilement les fonctions du module Array qui transforment les tableaux à l'aide de l'opérateur de pipeline (|>), comme indiqué dans l'exemple suivant.
[| 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"
Le résultat est le suivant :
[|100; 36; 16; 4|]
Tableaux multidimensionnels
Un tableau multidimensionnel peut être créé, mais aucune syntaxe n'existe pour l'écriture d'un littéral de tableau multidimensionnel. Utilisez l'opérateur array2D pour créer un tableau à partir d'une séquence de séquences d'éléments de tableau. Les séquences peuvent être des littéraux de listes ou de tableaux. Par exemple, le code suivant crée un tableau à deux dimensions.
let my2DArray = array2D [ [ 1; 0]; [0; 1] ]
Vous pouvez également utiliser la fonction Array2D.init pour initialiser des tableaux de deux dimensions ; sachant que des fonctions semblables sont disponibles pour les tableaux de trois et quatre dimensions. Ces fonctions prennent une fonction utilisée pour créer les éléments. Pour créer un tableau à deux dimensions qui contient des éléments ayant une valeur initiale plutôt que de spécifier une fonction, utilisez la fonction Array2D.create, qui est également disponible pour les tableaux jusqu'à quatre dimensions. L'exemple de code suivant illustre d'abord la création d'un tableau de tableaux qui contient les éléments souhaités, puis utilise Array2D.init pour générer le tableau à deux dimensions souhaité.
let arrayOfArrays = [| [| 1.0; 0.0 |]; [|0.0; 1.0 |] |]
let twoDimensionalArray = Array2D.init 2 2 (fun i j -> arrayOfArrays.[i].[j])
La syntaxe d'indexation et de découpage de tableau est prise en charge pour les tableaux allant jusqu'au rang 4. Lorsque vous spécifiez un index dans plusieurs dimensions, vous utilisez des virgules pour séparer les index, comme illustré dans l'exemple de code suivant.
twoDimensionalArray.[0, 1] <- 1.0
Le type d'un tableau à deux dimensions s'écrit <type>[,] (par exemple,int[,], double[,]), le type d'un tableau tridimensionnel s'écrit <type>[,,], et ainsi de suite pour les tableaux de dimensions supérieures.
Seul un sous-ensemble des fonctions disponibles pour les tableaux unidimensionnels est également disponible pour les tableaux multidimensionnels. Pour plus d'informations, consultez Collections.Array, module (F#), Collections.Array2D, module (F#), Collections.Array3D, module (F#) et Collections.Array4D, module (F#).
Découpage de tableau et tableaux multidimensionnels
Dans un tableau à deux dimensions (une matrice), vous pouvez extraire une sous-matrice en spécifiant des plages et en utilisant un caractère générique (*) pour spécifier des lignes ou colonnes entières.
// 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]
Avec F# 3,1, vous pouvez décomposer un tableau multidimensionnel en sous-tableaux de même dimension ou de dimension inférieure. Par exemple, vous pouvez obtenir un vecteur à partir d'une matrice en spécifiant une ligne ou une colonne unique.
// Get row 3 from a matrix as a vector:
matrix.[3, *]
// Get column 3 from a matrix as a vector:
matrix.[*, 3]
Vous pouvez utiliser cette syntaxe de découpage pour les types qui implémentent les opérateurs d'accès d'élément et les méthodes GetSlice surchargées. Par exemple, le code suivant crée un type Matrix qui encapsule le tableau 2D F#, implémente une propriété Item pour prendre en charge l'indexation de tableau et implémente trois versions de GetSlice. Si vous pouvez utiliser ce code comme modèle pour vos types de matrice, vous pouvez utiliser toutes les opérations de découpage décrites dans cette section.
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 "%A" submatrix
let firstRow = test1.[0,*]
let secondRow = test1.[1,*]
let firstCol = test1.[*,0]
printfn "%A" firstCol
Fonctions booléennes sur les tableaux
Les fonctions Array.exists et Array.exists2 testent des éléments dans un ou deux tableaux, respectivement. Ces fonctions prennent une fonction de test et retournent la valeur true si l'un des éléments (ou des paires d'éléments pour Array.exists2) satisfait à la condition.
Le code suivant illustre l'utilisation d'Array.exists et d'Array.exists2. Dans ces exemples, les nouvelles fonctions sont créées en appliquant un seul des arguments, dans ces cas, l'argument de fonction.
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|])
Le résultat du code précédent est comme suit.
true
false
false
true
De même, la fonction Array.forall teste un tableau pour déterminer si chaque élément satisfait à une condition booléenne. La variation Array.forall2 fait la même chose à l'aide d'une fonction booléenne qui implique les éléments de deux tableaux de longueur égale. Le code suivant illustre l'utilisation de ces fonctions.
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 |])
Le résultat de ces exemples est le suivant.
false
true
true
false
Recherche de tableaux
Array.find prend une fonction booléenne et retourne le premier élément pour lequel la fonction retourne la valeur true, ou lève une KeyNotFoundException si aucun élément ne satisfait à la condition. Array.findIndex est comme Array.find, mais retourne l'index de l'élément au lieu de l'élément lui-même.
Le code suivant utilise Array.find et Array.findIndex pour trouver un nombre qui est à la fois un carré parfait et un cube parfait.
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
La sortie est la suivante.
The first element that is both a square and a cube is 64 and its index is 62.
Array.tryFind est comme Array.find, mais son résultat est un type d'option, et elle retourne None si aucun élément n'est trouvé. Array.tryFind doit être utilisée à la place d'Array.find lorsque vous ne savez pas si un élément correspondant se trouve dans le tableau. De même, Array.tryFindIndex est comme Array.findIndex, mais le type d'option est la valeur de retour. Si aucun élément n'est trouvé, l'option est None.
Le code suivant illustre l'utilisation de Array.tryFind. Ce code dépend du code précédent.
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 |]
La sortie est la suivante.
Found an element: 1
Found an element: 729
Utilisez Array.tryPick lorsque vous devez transformer un élément en plus de le trouver. Le résultat est le premier élément pour lequel la fonction retourne l'élément transformé comme valeur d'option, ou None si aucun élément n'est trouvé.
Le code suivant illustre l'utilisation d'Array.tryPick. Dans ce cas, au lieu d'une expression lambda, plusieurs fonctions d'assistance locales sont définies pour simplifier le code.
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 |]
La sortie est la suivante.
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.
Exécution de calculs sur des tableaux
La fonction Array.average retourne la moyenne de chaque élément d'un tableau. Elle est limitée aux types d'élément qui prennent en charge la division exacte par un entier, ce qui inclut les types à virgule flottante, mais pas les types intégraux. La fonction Array.averageBy retourne la moyenne des résultats de l'appel d'une fonction sur chaque élément. Pour un tableau de type intégral, vous pouvez utiliser Array.averageBy pour que la fonction convertisse chaque élément en type à virgule flottante pour le calcul.
Utilisez Array.max ou Array.min pour obtenir l'élément maximal ou minimal, si le type d'élément prend en charge cette opération. De même, Array.maxBy et Array.minBy autorisent l'exécution d'une fonction en premier, par exemple pour effectuer une transformation vers un type qui prend en charge la comparaison.
Array.sum ajoute les éléments d'un tableau et Array.sumBy appelle une fonction sur chaque élément et ajoute les résultats ensemble.
Pour exécuter une fonction sur chaque élément d'un tableau sans stocker les valeurs de retour, utilisez Array.iter. Pour appliquer une fonction qui implique deux tableaux de longueur égale, utilisez Array.iter2. Si vous devez également garder un tableau des résultats de la fonction, utilisez Array.map ou Array.map2, qui fonctionne sur deux tableaux à la fois.
Les variations Array.iteri et Array.iteri2 autorisent l'index de l'élément à être impliqué dans le calcul ; même chose pour Array.mapi et Array.mapi2.
Les fonctions Array.fold, Array.foldBack, Array.reduce, Array.reduceBack, Array.scan et Array.scanBack exécutent des algorithmes qui impliquent tous les éléments d'un tableau. De même, les variations Array.fold2 et Array.foldBack2 exécutent des calculs sur deux tableaux.
Ces fonctions permettant l'exécution de calculs correspondent aux fonctions du même nom dans le module List. Pour obtenir des exemples d'utilisation, consultez Listes (F#).
Modification de tableaux
Array.set affecte à un élément une valeur spécifiée. Array.fill affecte à une plage d'éléments d'un tableau une valeur spécifiée. Le code suivant illustre Array.fill.
let arrayFill1 = [| 1 .. 25 |]
Array.fill arrayFill1 2 20 0
printfn "%A" arrayFill1
La sortie est la suivante.
[|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|]
Vous pouvez utiliser Array.blit pour copier une sous-section d'un tableau dans un autre tableau.
Conversion vers et à partir d'autres types
Array.ofList crée un tableau à partir d'une liste. Array.ofSeq crée un tableau à partir d'une séquence. Array.toList et Array.toSeq effectuent la conversion vers ces autres types de collection à partir du type de tableau.
Tri de tableaux
Utilisez Array.sort pour trier un tableau à l'aide de la fonction de comparaison générique. Utilisez Array.sortBy pour spécifier une fonction qui génère une valeur, connue sous le nom de clé, pour trier en utilisant la fonction de comparaison générique sur la clé. Utilisez Array.sortWith si vous souhaitez fournir une fonction de comparaison personnalisée. Les fonctions Array.sort, Array.sortBy et Array.sortWith retournent toutes le tableau trié comme nouveau tableau. Les variations Array.sortInPlace, Array.sortInPlaceBy et Array.sortInPlaceWith modifient le tableau existant au lieu d'en retourner un nouveau.
Tableaux et tuples
Les fonctions Array.zip et Array.unzip convertissent des tableaux de paires de tuples en tuples de tableaux et vice versa. Array.zip3 et Array.unzip3 sont semblables, mais fonctionnent avec des tuples de trois éléments ou des tuples de trois tableaux.
Calculs parallèles sur les tableaux
Le module Array.Parallel contient des fonctions pour l'exécution de calculs parallèles sur les tableaux. Ce module n'est pas disponible dans les applications qui ciblent les versions du .NET Framework antérieures à la version 4.