OCaml - 从一维数组到二维数组的转换

OCaml - Conversion from one-dimensional array to two-dimensional array

我正在尝试在 OCaml 中将一维数组转换为二维数组。

测试我写的功能时:

# to_array2d [|1;2;3;1;2;4;1;2;5|];;

我得到这个不正确的结果:

int array array = [|[|1; 2; 5|]; [|1; 2; 5|]; [|1; 2; 5|]|]

正确的结果应该是:

int array array = [|[|1; 2; 3|]; [|1; 2; 4|]; [|1; 2; 5|]|]

代码如下:

let to_array2d (array1d: int array) : int array array = 
    let dim = int_of_float (sqrt (float (Array.length array1d))) in
        let array2d = Array.make dim (Array.make dim 0 ) in
            for i = 0 to (dim - 1) do 
                    for j = 0 to (dim - 1) do
                        array2d.(i).(j) <- (Array.get array1d (i * dim + j))
                    done
                done;
                array2d
;;

我做错了什么?

您错误地创建了二维数组。 Array.make 只会复制第二个参数 dim 次。所以,你有一个包含三个指向同一个数组的指针的数组。查看 以了解有关如何正确烘焙数组的更多详细信息。使用 Array.init 函数

可以更好地编写您的特定案例
let to_array2d (array1d: int array) : int array array = 
  let dim = int_of_float (sqrt (float (Array.length array1d))) in
  Array.init dim (fun i -> Array.init dim (fun j -> array1d.(i * dim + j)))

不过,我不喜欢 sqrt 长度的想法。

可以在 OCaml Book 中找到对这个问题的很好的解释。看第68页(pdf的第94页)。

详细说明@ivg 的回答:Array.make 将获取您提供的值并将其放入新数组的所有元素中。对于具有结构的值(如数组),这意味着您最终会出现三个非常 相同的 值。

# let a = Array.make 3 (Array.make 4 0);;
 val a : int array array = [|[|0; 0; 0; 0|]; [|0; 0; 0; 0|]; [|0; 0; 0; 0|]|]
# a.(1).(3) <- 17;;
- : unit = ()
# a;;
- : int array array = [|[|0; 0; 0; 17|]; [|0; 0; 0; 17|]; [|0; 0; 0; 17|]|]
# 

您不希望出现完全相同的数组。你每次都想要一个不同的数组。为此,您可以使用 Array.init (例如)。

# let a = Array.init 3 (fun _ -> Array.make 4 0);;
val a : int array array = [|[|0; 0; 0; 0|]; [|0; 0; 0; 0|]; [|0; 0; 0; 0|]|]
# a.(1).(3) <- 17;;
- : unit = ()
# a;;
- : int array array = [|[|0; 0; 0; 0|]; [|0; 0; 0; 17|]; [|0; 0; 0; 0|]|]

你的代码错误的地方是

let b =
  Array.make dim (Array.make dim 0)

这不是您想要的。名称 b 不在您的代码中,但方便讨论。为了理解您所看到的,让我们以下列等效方式重写这段代码:

let b =
  let a = Array.make dim 0 in
  Array.make dim a

此代码生成一个长度为 dim 的数组,其条目均为 a。这些不是 a 的副本,它们只是 a 的不同名称。在 OCaml 中表达这一点的正确方法是说这些结构 物理上相等 并且 == 运算符测试物理相等性。

# b.(0) == b.(1);;
true

物理相等性是比更常用的 = 运算符测试的结构相等性更强的关系。指定给定两个物理上相等的可变结构,如 b.(0)b.(1),其中任何一个的修改也会影响另一个,或者用 documentation of the Pervasives module 的话说:

val (==) : 'a -> 'a -> bool e1 == e2 tests for physical equality of e1 and e2. On mutable types such as references, arrays, byte sequences, records with mutable fields and objects with mutable instance variables, e1 == e2 is true if and only if physical modification of e1 also affects e2. On non-mutable types, the behavior of ( == ) is implementation-dependent; however, it is guaranteed that e1 == e2 implies compare e1 e2 = 0.

我们可以认为这是一种正式的方式,可以说这两个结构“真的一样”。

如果您想修改代码的结构,可以利用 Array.make_matrix 函数,它会生成一个新的二维数组。如果您厌倦了调试 for 循环中的错误,您可以使用更像 ocaml 的解决方案:

let unpack dim a =
  let line i =
    Array.init dim (fun j -> a.(i*dim + j))
  in
  Array.init dim line

let to_array2d a =
  let dim = int_of_float (sqrt (float (Array.length array1d))) in
  unpack dim a

另请参阅

  • What's the difference between "equal (=)" and "identical (==)" in ocaml?
  • Does != have meaning in OCaml?