陣列 (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 包含的函式分別支援二維、三維和四維陣列的作業。您可以使用 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 會出現在兩個陣列中,原因是 StringBuilder 型別上的 Insert 作業會影響基礎 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。
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!"
您可以使用管線運算子 (|>),輕鬆合併陣列模組中用來轉換陣列的函式,如下列範例所示。
[| 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>[,,],更高維度的陣列以此類推。
一維陣列可用的函式只有部分可用於多維陣列。如需詳細資訊,請參閱 Collections.Array 模組 (F#)、Collections.Array2D 模組 (F#)、Collections.Array3D 模組 (F#) 和 Collections.Array4D 模組 (F#)。
陣列的布林函式
Array.exists 和 Array.exists2 函式會分別測試一個或兩個陣列中的項目。這些函式採用測試函式,並在有項目 (或 Array.exists2 的項目配對) 滿足條件時傳回 true。
下列程式碼會示範 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 類似,差異在於它會產生選項型別,並在找不到項目時傳回 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
當您需要轉換和尋找項目時,請使用 Array.tryPick。如果函式傳回轉換後的元素做為選項值,則結果為第一個元素;如果找不到這類元素,則為 None。
下列程式碼顯示如何使用 Array.tryPick。在此情況下,已定義數種本機 Helper 函式來簡化程式碼,而非 Lambda 運算式。
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 變化也會對兩個陣列執行計算。
這些執行計算的函式對應於清單模組中同名的函式。如需使用範例,請參閱清單 (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 可以指定會產生值 (稱為「索引鍵」(Key)) 的函式,以針對索引鍵使用泛型比較函式來進行排序。如果您想要提供自訂比較函式,請使用 Array.sortWith。Array.sort、Array.sortBy 和 Array.sortWith 都會傳回已排序陣列做為新陣列。Array.sortInPlace、Array.sortInPlaceBy 和 Array.sortInPlaceWith 變化會修改現有陣列,而非傳回新陣列。
陣列和 Tuple
Array.zip 和 Array.unzip 函式會將 Tuple 配對的陣列轉換為陣列 Tuple,反之亦然。Array.zip3 和 Array.unzip3 類似,差異在於它們會使用三個項目的 Tuple 或三個陣列的 Tuple。
陣列的平行計算
Array.Parallel 模組包含在陣列上執行平行計算的函式。這個模組不適用於以 .NET Framework 第 4 版之前版本為目標的應用程式。