陣列

簡介

在 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_leftArray.fold_right 函式。這些函式會接受一個二元函式、一個初始累加器值和一個陣列作為參數。二元函式會接受兩個參數:累加器的目前值和陣列的目前元素,然後傳回一個新的累加器值。兩個函式都會遍歷陣列,但方向相反。這基本上與 List.fold_leftList.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 模組的完整函數清單。

仍然需要協助嗎?

協助改進我們的文件

所有 OCaml 文件都是開放原始碼。看到錯誤或不清楚的地方嗎?提交 pull request。

OCaml

創新。社群。安全。