OCaml - 从一维数组转换为二维数组

时间:2015-03-29 17:16:55

标签: ocaml

我试图将一维数组转换为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
;;

我做错了什么?

3 个答案:

答案 0 :(得分:3)

代码中的错误位置是

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测试e1e2的物理平等。在可变类型(如引用,数组,字节序列,具有可变字段的记录和具有可变实例变量的对象)上,当{且仅e1 == e2的物理修改也影响e1时,e2为真。在非可变类型上,( == )的行为依赖于实现;但是,保证e1 == e2暗示比较e1 e2 = 0

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

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

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

另见

答案 1 :(得分:2)

您错误地创建了2D数组。 Array.make只会复制第二个参数dim次。所以,你有一个指向同一个数组的三个指针的数组。查看here以获取有关如何正确烹饪阵列的更多详细信息。使用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页)。

答案 2 :(得分:2)

详细说明@ 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|]|]