陣列
簡介
在 OCaml 中,陣列是同一類型元素的集合。與串列不同,陣列可以透過將元素替換為相同類型的其他值來變更,但無法調整大小。陣列也允許有效率地存取任何位置的元素。
儘管存在這些差異,許多在陣列上可用的函式與串列可用的函式相似。請參閱串列教學和文件以獲取有關這些函式的更多詳細資訊。
本教學旨在介紹 OCaml 中陣列的主題,並展示最有用的函式和使用案例。
陣列在 OCaml 中常用於以下任務:
- 儲存和處理大量資料
- 實作需要隨機存取和修改元素的演算法
- 處理矩陣和其他多維資料結構
建立陣列
若要在 OCaml 中建立陣列,您可以使用 [| ...; ... |]
語法,此語法允許您直接指定每個元素的值。例如,若要建立一個包含值 1、2、3、4 和 5 的陣列,您將寫入 [| 1; 2; 3; 4; 5 |]
# [| 1; 2; 3; 4; 5 |];;
- : int array = [|1; 2; 3; 4; 5|]
或者,您可以使用 Array.make
函式建立陣列,該函式接受兩個參數:陣列的長度和每個元素的初始值。例如,若要建立一個長度為 5 的陣列,且所有元素初始化為 0,您可以寫入
# let zeroes = Array.make 5 0;;
val zeroes : int array = [|0; 0; 0; 0; 0|]
Array.init
會產生一個指定長度的陣列,方法是對陣列的每個索引(從 0 開始)應用一個函式。以下程式碼行使用將其參數加倍的函式,建立一個包含前 5 個偶數的陣列
# let even_numbers = Array.init 5 (fun i -> i * 2);;
val even_numbers : int array = [|0; 2; 4; 6; 8|]
存取陣列元素
您可以使用 .(index)
語法存取陣列的個別元素,其中索引是您要存取元素的索引。第一個元素的索引為 0,最後一個元素的索引比陣列大小少 1。例如,若要存取陣列 even_numbers
的第三個元素,您會寫入
# even_numbers.(2);;
- : int = 4
修改陣列元素
若要修改陣列中的元素,我們只需使用索引運算子為其指派一個新值。例如,若要將上面建立的陣列 even_numbers
的第三個元素的值變更為 42,我們必須寫入
# even_numbers.(2) <- 42;;
- : unit = ()
請注意,此操作會傳回 unit
,而不是修改後的陣列。 even_numbers
會就地修改,作為副作用。
Array
模組
標準函式庫 OCaml 提供幾個用於處理陣列的實用函式。以下是一些最常見的函式
陣列長度
Array.length
函式會傳回陣列的大小
# Array.length even_numbers;;
- : int = 5
迭代陣列
Array.iter
會將函式一次應用於陣列的每個元素。給定的函式必須傳回 unit
,透過副作用運作。若要列印上面建立的陣列 zeroes
的所有元素,我們可以將 print_int
應用於每個元素
# Array.iter (fun x -> print_int x; print_string " ") zeroes;;
0 0 0 0 0 - : unit = ()
也可以使用 for
迴圈來迭代陣列。以下是使用迴圈的相同範例
# for i = 0 to Array.length zeroes - 1 do
print_int zeroes.(i);
print_string " "
done;;
0 0 0 0 0 - : unit = ()
映射陣列
Array.map
函式會將給定的函式應用於陣列的每個元素,以建立一個新的陣列。例如,我們可以取得一個包含 even_numbers
陣列中每個數字的平方的陣列
# Array.map (fun x -> x * x) even_numbers;;
- : int array = [|0; 4; 1764; 36; 64|]
摺疊陣列
若要將陣列的所有元素組合成單一結果,我們可以使用 Array.fold_left
和 Array.fold_right
函式。這些函式會接受一個二元函式、一個初始累加器值和一個陣列作為參數。二元函式會接受兩個參數:累加器的目前值和陣列的目前元素,然後傳回一個新的累加器值。兩個函式都會遍歷陣列,但方向相反。這基本上與 List.fold_left
和 List.fold_right
相同。
以下是 Array.fold_left
的簽名
# Array.fold_left;;
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b array -> 'a = <fun>
fold_left f init a
會計算 f (... (f(f init a.(0)) a.(1)) ...) a.(n-1)
類似地,我們可以使用 Array.fold_right
函式,該函式會切換其參數的順序
# Array.fold_right;;
val fold_right : ('b -> 'a -> 'a) -> 'b array -> 'a -> 'a = <fun>
fold_right f a init
會計算 f a.(0) (f a.(1) ( ... (f a.(n-1) init) ...))
這些函式會從整個陣列推導出單一值。例如,它們可用於尋找陣列的最大元素
# Array.fold_left Int.max min_int even_numbers;;
- : int = 42
排序陣列
若要排序陣列,我們可以使用 Array.sort
函式。此函式會接受以下參數
- 一個比較函式
- 一個陣列。它會根據提供的比較函式,對提供的陣列進行就地且依遞增順序排序。由
Array.sort
執行的排序會修改提供的陣列的內容,這就是為什麼它會傳回unit
的原因。例如,若要排序上面建立的陣列even_numbers
,我們可以執行以下操作
# Array.sort compare even_numbers;;
- : unit = ()
# even_numbers;;
- : int array = [|0; 2; 6; 8; 42|]
將陣列的一部分複製到另一個陣列
Array.blit
函數能有效地將陣列的連續部分複製到另一個陣列中。類似於 array.(x) <- y
操作,此函數會原地修改目標陣列並返回 unit
,而不是修改後的陣列。假設您想將 ones
的一部分複製到 zeroes
中
# let ones = Array.make 5 1;;
val ones : int array = [|1; 1; 1; 1; 1|]
# Array.blit ones 0 zeroes 1 2;;
- : unit = ()
# zeroes;;
- : int array = [|0; 1; 1; 0; 0|]
這會將 ones
中從索引 0
開始的兩個元素 (此陣列切片為 [| 1; 1 |]
) 複製到 zeroes
中,從索引 1
開始。您有責任確保提供的兩個索引在其各自的陣列中有效,且要複製的元素數量在每個陣列的範圍內。
我們也可以使用此函數將陣列的一部分複製到自身
# Array.blit zeroes 1 zeroes 3 2;;
- : unit = ()
# zeroes;;
- : int array = [|0; 1; 1; 1; 1|]
這會將 zeroes
中從索引 1
開始的兩個元素複製到 zeroes
的最後一部分,從索引 3
開始。
結論
在本教學中,我們介紹了 OCaml 中陣列的基本知識,包括如何建立和操作它們,以及一些最有用的函數和使用案例。請參閱標準函式庫文件,以瀏覽 Array
模組的完整函數清單。