Matriser (F#)
Matriser är fasta, nollbaserade, föränderliga samlingar av efterföljande dataelement som alla är av samma typ.
Skapa matriser
Du kan skapa matriser på flera sätt. Du kan skapa en liten matris genom att visa på varandra följande värden mellan [|
och |]
avgränsade med semikolon, som du ser i följande exempel.
let array1 = [| 1; 2; 3 |]
Du kan också placera varje element på en separat rad, i vilket fall semikolonavgränsaren är valfri.
let array1 =
[|
1
2
3
|]
Typen av matriselement härleds från de literaler som används och måste vara konsekventa.
// 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 |]
Följande kod orsakar ett fel eftersom 3.0 är en flyttal och 1 och 2 är heltal.
// Causes an error. The 3.0 (float) cannot be converted to integer implicitly.
// let array3 = [| 1; 2; 3.0 |]
Följande kod orsakar också ett fel eftersom 1,2
är en tuppeln och 3
är ett heltal.
// Causes an error too. The 3 (integer) cannot be converted to tuple implicitly.
// let array4 = [| 1, 2; 3 |]
Du kan också använda sekvensuttryck för att skapa matriser. Följande är ett exempel som skapar en matris med heltal från 1 till 10.
let array3 = [| for i in 1 .. 10 -> i * i |]
Om du vill skapa en matris där alla element initieras till noll använder du Array.zeroCreate
.
let arrayOfTenZeroes : int array = Array.zeroCreate 10
Åtkomstelement
Du kan komma åt matriselement med hjälp av hakparenteser ([
och ]
). Den ursprungliga punktsyntaxen (.[index]
) stöds fortfarande men rekommenderas inte längre från och med F# 6.0.
array1[0]
Matrisindex börjar vid 0.
Du kan också komma åt matriselement med hjälp av segment notation, vilket gör att du kan ange en underordning av matrisen. Exempel på segment notation följer.
// 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..]
När segment notation används skapas en ny kopia av matrisen.
Matristyper och moduler
Typen av alla F#-matriser är .NET Framework-typen System.Array. Därför har F#-matriser stöd för alla funktioner som är tillgängliga i System.Array.
Modulen Array
stöder åtgärder på endimensionella matriser. Modulerna Array2D
, Array3D
och Array4D
innehåller funktioner som stöder åtgärder på matriser med två, tre respektive fyra dimensioner. Du kan skapa matriser med rangordning större än fyra med hjälp System.Arrayav .
Enkla funktioner
Array.get
hämtar ett element. Array.length
ger längden på en matris. Array.set
anger ett element till ett angivet värde. Följande kodexempel illustrerar användningen av dessa funktioner.
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)
Utdata är följande.
0 1 2 3 4 5 6 7 8 9
Funktioner som skapar matriser
Flera funktioner skapar matriser utan att kräva en befintlig matris. Array.empty
skapar en ny matris som inte innehåller några element. Array.create
skapar en matris med en angiven storlek och anger alla element till angivna värden. Array.init
skapar en matris, givet en dimension och en funktion för att generera elementen. Array.zeroCreate
skapar en matris där alla element initieras till nollvärdet för matrisens typ. Följande kod visar dessa funktioner.
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
Utdata är följande.
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
skapar en ny matris som innehåller element som kopieras från en befintlig matris. Observera att kopian är en ytlig kopia, vilket innebär att om elementtypen är en referenstyp kopieras endast referensen, inte det underliggande objektet. Följande kodexempel illustrerar detta.
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
Utdata från föregående kod är följande:
[|Test1; Test2; |]
[|; Test2; |]
Strängen Test1
visas bara i den första matrisen eftersom åtgärden att skapa ett nytt element skriver över referensen i firstArray
men inte påverkar den ursprungliga referensen till en tom sträng som fortfarande finns i secondArray
. Strängen Test2
visas i båda matriserna eftersom Insert
åtgärden på System.Text.StringBuilder typen påverkar det underliggande System.Text.StringBuilder objektet, som refereras till i båda matriserna.
Array.sub
genererar en ny matris från en underordnad matris. Du anger underordningen genom att ange startindexet och längden. Följande kod visar användningen av Array.sub
.
let a1 = [| 0 .. 99 |]
let a2 = Array.sub a1 5 10
printfn "%A" a2
Utdata visar att underordningen börjar på element 5 och innehåller 10 element.
[|5; 6; 7; 8; 9; 10; 11; 12; 13; 14|]
Array.append
skapar en ny matris genom att kombinera två befintliga matriser.
Följande kod visar Array.append.
printfn "%A" (Array.append [| 1; 2; 3|] [| 4; 5; 6|])
Utdata från föregående kod är följande.
[|1; 2; 3; 4; 5; 6|]
Array.choose
väljer element i en matris som ska inkluderas i en ny matris. Följande kod visar Array.choose
. Observera att elementtypen för matrisen inte behöver matcha typen av värde som returneras i alternativtypen. I det här exemplet är int
elementtypen och alternativet är resultatet av en polynomfunktion, , elem*elem - 1
som ett flyttalsnummer.
printfn "%A" (Array.choose (fun elem -> if elem % 2 = 0 then
Some(float (elem*elem - 1))
else
None) [| 1 .. 10 |])
Utdata från föregående kod är följande.
[|3.0; 15.0; 35.0; 63.0; 99.0|]
Array.collect
kör en angiven funktion på varje matriselement i en befintlig matris och samlar sedan in de element som genereras av funktionen och kombinerar dem till en ny matris. Följande kod visar Array.collect
.
printfn "%A" (Array.collect (fun elem -> [| 0 .. elem |]) [| 1; 5; 10|])
Utdata från föregående kod är följande.
[|0; 1; 0; 1; 2; 3; 4; 5; 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
Array.concat
tar en sekvens med matriser och kombinerar dem till en enda matris. Följande kod visar 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
tar en boolesk villkorsfunktion och genererar en ny matris som endast innehåller de element från indatamatrisen som villkoret är sant för. Följande kod visar Array.filter
.
printfn "%A" (Array.filter (fun elem -> elem % 2 = 0) [| 1 .. 10|])
Utdata från föregående kod är följande.
[|2; 4; 6; 8; 10|]
Array.rev
genererar en ny matris genom att ändra ordningen på en befintlig matris. Följande kod visar Array.rev
.
let stringReverse (s: string) =
System.String(Array.rev (s.ToCharArray()))
printfn "%A" (stringReverse("!dlrow olleH"))
Utdata från föregående kod är följande.
"Hello world!"
Du kan enkelt kombinera funktioner i matrismodulen som transformerar matriser med hjälp av pipelineoperatorn (|>
), som du ser i följande exempel.
[| 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"
Utdata är
[|100; 36; 16; 4|]
Flerdimensionella matriser
En flerdimensionell matris kan skapas, men det finns ingen syntax för att skriva en flerdimensionell matrisliteral. Använd operatorn array2D
för att skapa en matris från en sekvens med sekvenser av matriselement. Sekvenserna kan vara matriser eller listliteraler. Följande kod skapar till exempel en tvådimensionell matris.
let my2DArray = array2D [ [ 1; 0]; [0; 1] ]
Du kan också använda funktionen Array2D.init
för att initiera matriser med två dimensioner, och liknande funktioner är tillgängliga för matriser med tre och fyra dimensioner. Dessa funktioner använder en funktion som används för att skapa elementen. Om du vill skapa en tvådimensionell matris som innehåller element som är inställda på ett initialt värde i stället för att ange en funktion använder du Array2D.create
funktionen, som också är tillgänglig för matriser med upp till fyra dimensioner. I följande kodexempel visas först hur du skapar en matris med matriser som innehåller önskade element och sedan använder Array2D.init
för att generera den önskade tvådimensionella matrisen.
let arrayOfArrays = [| [| 1.0; 0.0 |]; [|0.0; 1.0 |] |]
let twoDimensionalArray = Array2D.init 2 2 (fun i j -> arrayOfArrays[i][j])
Matrisindexering och segmenteringssyntax stöds för matriser upp till rangordning 4. När du anger ett index i flera dimensioner använder du kommatecken för att separera indexen, enligt följande kodexempel.
twoDimensionalArray[0, 1] <- 1.0
Typen av en tvådimensionell matris skrivs ut som <type>[,]
(till exempel int[,]
, double[,]
), och typen av en tredimensionell matris skrivs som <type>[,,]
och så vidare för matriser med högre dimensioner.
Endast en delmängd av de funktioner som är tillgängliga för endimensionella matriser är också tillgängliga för flerdimensionella matriser.
Matrisslicering och flerdimensionella matriser
I en tvådimensionell matris (en matris) kan du extrahera en undermatris genom att ange intervall och använda ett jokertecken (*
) för att ange hela rader eller kolumner.
// 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]
Du kan dela upp en flerdimensionell matris i subarrayer med samma eller lägre dimension. Du kan till exempel hämta en vektor från en matris genom att ange en enskild rad eller kolumn.
// Get row 3 from a matrix as a vector:
matrix[3, *]
// Get column 3 from a matrix as a vector:
matrix[*, 3]
Du kan använda den här segmenteringssyntaxen för typer som implementerar elementåtkomstoperatorerna och överlagrade GetSlice
metoder. Följande kod skapar till exempel en matristyp som omsluter F# 2D-matrisen, implementerar en objektegenskap för att ge stöd för matrisindexering och implementerar tre versioner av GetSlice
. Om du kan använda den här koden som en mall för dina matristyper kan du använda alla segmenteringsåtgärder som beskrivs i det här avsnittet.
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}"
Booleska funktioner i matriser
Funktionerna Array.exists
och Array.exists2
testelementen i antingen en eller två matriser. Dessa funktioner utför en testfunktion och returnerar true
om det finns ett element (eller elementpar för Array.exists2
) som uppfyller villkoret.
Följande kod visar användningen av Array.exists
och Array.exists2
. I de här exemplen skapas nya funktioner genom att endast tillämpa ett av argumenten, i dessa fall funktionsargumentet.
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|])
Utdata från föregående kod är följande.
true
false
false
true
På samma sätt testar funktionen Array.forall
en matris för att avgöra om varje element uppfyller ett booleskt villkor. Varianten Array.forall2
gör samma sak med hjälp av en boolesk funktion som omfattar element med två matriser med samma längd. Följande kod illustrerar användningen av dessa funktioner.
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 |])
Utdata för dessa exempel är följande.
false
true
true
false
Sökmatriser
Array.find
tar en boolesk funktion och returnerar det första elementet som funktionen returnerar true
eller genererar ett System.Collections.Generic.KeyNotFoundException om inget element som uppfyller villkoret hittas. Array.findIndex
är som Array.find
, förutom att det returnerar indexet för elementet i stället för själva elementet.
Följande kod använder Array.find
och Array.findIndex
för att hitta ett tal som är både en perfekt kvadratisk och perfekt kub.
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
Utdata är följande.
The first element that is both a square and a cube is 64 and its index is 62.
Array.tryFind
är som Array.find
, förutom att resultatet är en alternativtyp och returnerar None
om inget element hittas. Array.tryFind
ska användas i stället för Array.find
när du inte vet om ett matchande element finns i matrisen. Array.tryFindIndex
På samma sätt är det som Array.findIndex
förutom att alternativtypen är returvärdet. Om inget element hittas är None
alternativet .
Följande kod visar användningen av Array.tryFind
. Den här koden beror på den tidigare koden.
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 |]
Utdata är följande.
Found an element: 1
Found an element: 729
Failed to find a matching element.
Använd Array.tryPick
när du behöver transformera ett element förutom att hitta det. Resultatet är det första element för vilket funktionen returnerar transformerade elementet som ett alternativvärde, eller None
om inget sådant element hittas.
Följande kod visar användningen av Array.tryPick
. I det här fallet definieras flera lokala hjälpfunktioner i stället för ett lambda-uttryck för att förenkla koden.
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 |]
Utdata är följande.
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.
Utföra beräkningar på matriser
Funktionen Array.average
returnerar medelvärdet för varje element i en matris. Den är begränsad till elementtyper som stöder exakt division av ett heltal, som innehåller flyttaltyper men inte integraltyper. Funktionen Array.averageBy
returnerar medelvärdet av resultatet av att anropa en funktion för varje element. För en matris av integrerad typ kan du använda Array.averageBy
och låta funktionen konvertera varje element till en flyttaltyp för beräkningen.
Använd Array.max
eller Array.min
för att hämta det maximala eller lägsta elementet om elementtypen stöder det. Array.maxBy
På samma sätt och Array.minBy
låt en funktion köras först, kanske för att transformera till en typ som stöder jämförelse.
Array.sum
lägger till elementen i en matris och Array.sumBy
anropar en funktion på varje element och lägger till resultaten tillsammans.
Om du vill köra en funktion på varje element i en matris utan att lagra returvärdena använder du Array.iter
. För en funktion som omfattar två matriser med samma längd använder du Array.iter2
. Om du också behöver behålla en matris med resultatet av funktionen använder Array.map
du eller Array.map2
, som körs på två matriser i taget.
Variationerna Array.iteri
och Array.iteri2
gör att indexet för elementet kan ingå i beräkningen. Detsamma gäller för Array.mapi
och Array.mapi2
.
Funktionerna Array.fold
, Array.foldBack
, Array.reduce
, Array.reduceBack
, Array.scan
och Array.scanBack
kör algoritmer som omfattar alla element i en matris. På samma sätt utför varianterna Array.fold2
och Array.foldBack2
beräkningarna på två matriser.
Dessa funktioner för att utföra beräkningar motsvarar funktionerna med samma namn i listmodulen. Användningsexempel finns i Listor.
Ändra matriser
Array.set
anger ett element till ett angivet värde. Array.fill
anger ett område med element i en matris till ett angivet värde. Följande kod innehåller ett exempel på Array.fill
.
let arrayFill1 = [| 1 .. 25 |]
Array.fill arrayFill1 2 20 0
printfn "%A" arrayFill1
Utdata är följande.
[|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|]
Du kan använda Array.blit
för att kopiera ett underavsnitt av en matris till en annan matris.
Konvertera till och från andra typer
Array.ofList
skapar en matris från en lista. Array.ofSeq
skapar en matris från en sekvens. Array.toList
och Array.toSeq
konvertera till dessa andra samlingstyper från matristypen.
Sortera matriser
Använd Array.sort
för att sortera en matris med hjälp av den allmänna jämförelsefunktionen. Använd Array.sortBy
för att ange en funktion som genererar ett värde, som kallas för en nyckel, för att sortera med hjälp av den allmänna jämförelsefunktionen på nyckeln. Använd Array.sortWith
om du vill ange en anpassad jämförelsefunktion. Array.sort
, Array.sortBy
, och Array.sortWith
alla returnerar den sorterade matrisen som en ny matris. Varianterna Array.sortInPlace
, Array.sortInPlaceBy
och Array.sortInPlaceWith
ändrar den befintliga matrisen i stället för att returnera en ny.
Matriser och tupplar
Funktionerna Array.zip
och Array.unzip
konvertera matriser av tupplar par till tupplar av matriser och vice versa. Array.zip3
och Array.unzip3
är liknande förutom att de fungerar med tupplar med tre element eller tupplar med tre matriser.
Parallella beräkningar på matriser
Modulen Array.Parallel
innehåller funktioner för att utföra parallella beräkningar på matriser. Den här modulen är inte tillgänglig i program som riktar in sig på versioner av .NET Framework före version 4.