【问题标题】:OCaml - Conversion from one-dimensional array to two-dimensional arrayOCaml - 从一维数组转换为二维数组
【发布时间】:2015-03-29 17:16:54
【问题描述】:

我正在尝试在 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
;;

我做错了什么?

【问题讨论】:

  • 嗨,Thomas,如果您明确写出您期望的输出,也许您的问题会更容易阅读。此外,也许您可​​以在代码之前移动输入、预期输出和实际输出,这可能是一个更好的标语。 :)

标签: ocaml


【解决方案1】:

你的代码有问题的地方是

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 -&gt; 'a -&gt; 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

另见

【讨论】:

  • 感谢您提供详细信息和更好的结构化解决方案!一件事:let dim = int_of_float (sqrt (float (Array.length array1d))) in 应该是:let dim = int_of_float (sqrt (float (Array.length a))) in
  • 感谢您指出这一点。也许这已经足够明显了,因此实际上不需要进行编辑。
【解决方案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|]|]

【讨论】:

  • 描述这种现象的关键概念是物理平等,您似乎指的是,没有命名它。
  • 是的,非常正确。试图保持简单。你可以在这里找到我对身体平等的看法:stackoverflow.com/questions/13590307/… 或在这里:stackoverflow.com/questions/29121354/…
  • 在我对这个问题的回答中,有一个指向该问题的链接以及您的回答。多么糟糕的表达方式! ;)
【解决方案3】:

您创建的二维数组不正确。 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)))

虽然,我不喜欢 sqrting 长度的想法。

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

【讨论】:

  • 实际上,参数并没有被复制(这几乎就是 OP 的代码不能按他预期的那样工作的原因。)此外,OCaml 中没有指针之类的东西,相关概念是物理上的平等
  • 顺便说一句,我也不是sqrt 的忠实粉丝。 :)
  • 好吧,考虑到它的背景,我正在尝试解决 OP,但没有引入许多新概念。使用“指针”一词会瞬间触发正确的联想。关于物理平等,这是一个更复杂的概念。严格来说,它表示,当且仅当一个对象的物理修改影响另一个对象时,对象在物理上是相等的。而OCaml Book中使用指针的思想来描述这个问题。
猜你喜欢
  • 2016-02-18
  • 2018-02-19
  • 2015-09-03
  • 2020-03-26
  • 2021-11-23
  • 2021-07-20
相关资源
最近更新 更多