Julia:为什么需要在重塑数组后收集输出?

Julia: Why is it necessary to collect the output after reshaping an Array?

A = collect(reshape(1:16, 4, 4))
4×4 Array{Int64,2}:
 1  5   9  13
 2  6  10  14
 3  7  11  15
 4  8  12  16

怎么有必要collect?不能reshape自动输出一个4x4 Array{Int64, 2}?在哪些情况下我需要 Base.ReshapedArray{Int64,2,UnitRange{Int64},Tuple{}}?

这里的关键是 reshape 通常提供对原始数据结构的 视图 ……它只是将您查看该数据的方式改变为新的形状。在这种情况下,您的原始数据结构只是范围 1:16.

范围是非常紧凑和高效的类似数组的对象。他们不需要存储所有元素;相反,他们只是做基本的数学运算来动态计算每个元素。通过这种方式,它们实际上 Array 等价物(如 collect(1:16))快 。在大多数情况下,您可以在 Array 工作的任何地方使用范围。这是为什么你不应该在函数签名中指定 arg::Array::Matrix 的主要原因——相反你(和其他库作者)应该使用 ::AbstractArray::AbstractMatrix 如果你不需要内存中的表示。

有两个主要例外:范围是完全不可变的——您不能赋值或修改它们的任何元素。所以这就是为什么您可能想要 collect 变成 Array 的原因之一。另一种情况是,如果您正在调用不支持 Julia 的通用灵活性的 C/Fortran/external 库。在这些情况下,他们通常需要像 Array.

这样的内存表示

reshape(1:16, 4, 4)也是如此。类似于范围如何即时计算它们的元素,在这种情况下,ReshapedArray 将您传递给它的索引转换回原始数组 (1:16) 的原始形状,然后它访问那个原始数组。它非常有效地做到这一点。事实上,如果你在幕后偷看,你会发现它只存储了 4 个整数:

julia> dump(reshape(1:16, 4, 4))
Base.ReshapedArray{Int64,2,UnitRange{Int64},Tuple{}}
  parent: UnitRange{Int64}
    start: Int64 1
    stop: Int64 16
  dims: Tuple{Int64,Int64}
    1: Int64 4
    2: Int64 4
  mi: Tuple{} ()

它只需要知道 UnitRange 父级 (1-16) 的起点和终点,以及它重塑为 (4x4) 的尺寸。其他一切都是按需完成的。正如您将注意到的,这里的大小无关紧要 — reshape(a:b, y, z) 将始终只存储 4 个整数,无论​​ a:b 有多大。