如何优雅地申明一堆相同大小和类型的数组?

我需要先申明很多相同大小和类型的数组,目前的方法总感觉不够好,即:

const NX, NY, NZ = 64, 64, 64
a1 = Array{Float64}(undef, NX, NY, NZ)
a2 = Array{Float64}(undef, NX, NY, NZ)
a3 = Array{Float64}(undef, NX, NY, NZ)
# ...

Fortran 可以很方便直接定义:

real(8), dimension(NX,NY,NZ) :: a1, a2, a3    ! 还有很多。。。

通过搜索,发现了这个回答 :Declaring multiple arrays in Julia - Stack Overflow ,可是里面的两个答案也觉得不够优雅。第一种用表达式的方法还是太繁琐;第二种即下面的方法稍微好一点:

a1, a2, a3, a4, a5 = [Array{Float64}(undef, NX, NY, NZ) for i=1:5]

但是如果有非常多的数组,用 for 循环向量会不会先占用非常大的内存并耗费更多的时间?而且如果增加新的数组,还需要修改后面的总数,总感觉不够方便。

请问 Julia 中是否有更简单、更优雅的方式申明多个相同大小和类型的数组?就和 Fortran 的方法类似。谢谢!

using Base.Cartesian
const NX, NY, NZ = 64, 64, 64
@nexprs 3 i -> a_i = Array{Float64}(undef, NX, NY, NZ)

这样能产生a_1,a_2,a_3变量,如果变量是从1到10就把3改为10就可以。缺点是不能产生没有下划线的变量。

1 个赞

非常感谢您的回答!

我其实想了解是否有比较通用的类似于 Fortran 的方法,这里 a1, a2, a3 其实只是示例,实际上的变量名之间没啥联系,比如是 ε0, ε, σ 之类的。

通过宏可以实现
例如ModelingToolkit.jl里的

@parameters x1, x2, x3

非常感谢!

为了申明一些预分配大小的数组,专门引用一个包,是不是有点浪费了。。

不过我简单搜索了下,MATLAB 和 Python 好像也都没有什么比较方便的方式来一行申明多个数组,或许是解释型语言的代价?

如果想定义一些不一样的变量名,可以这样

a, b, c, x, y, z = @ntuple 6 i -> Array{Float64}(undef, NX, NY, NZ)

同样需要使用Base.Cartesian

自己写个宏就行了

macro muldef(def::Expr, vars::Symbol...)
    defs = Expr(:block)
    for var in vars
        push!(defs.args, :($var=$def))
    end
    esc(defs)
end

#test
const NX, NY, NZ = 64, 64, 64

@muldef Array{Float64}(undef, NX, NY, NZ) a1 a2 a3
x, y, z, a, b, c = (Array{Float64}(undef, NX, NY, NZ) for _ in 1:6) 

生成器不会占更多的内存

只是举个例子,你可以参考楼上的写法自己写个宏或者直接用list comprehension。
这个不能叫代价反而是优势吧,正因为是动态语言所以不用像C或Fortran一样做什么都要先声明。

非常感谢各位的回答!

struct Foo end 
# only one allocation call is needed 
as = Array{Foo}(undef, k, nx, ny, nz)

# why would one need this 
for i = 1:k 
  @eval $(Symbol("a", i)) = view(as, $i, :, :, :)
end
1 个赞