How to resize a 2D or higher dimensional array?

in a module, can I define a unspecified dimension array first, and then in a function give its particular dimensions?
Like

global const a=Array{Float64,2}()

But obviously the above give me an array.
However, the point is, just define a 2d array first, then I will initialize its dimensions in a function f, like,

function f(i::Int64,j::int64)
    a=Array{Float64,2}(undef,i,j)
return nothing
end

Point is, in a module define a unspecified dimension 2d array a first, then in the function f give a particular dimensions, is it possible? If so how to do it?

I can think of the following

module Mod

global const a=Float64[]

function f(i::Int64,j::int64)
    resize!(a,i*j)
    reshape(a,(i,j))
return nothing
end

end

But is there better way?

You may ask why not just define the global array in the function f?
I can do this, but I would like to predefine it at the beginning of my module, so that I can see all the global variables at the beginning.

Also, I know we can do resize!, but resize! seems only works for 1D array.

Thanks in advance!

First. The global in global const is redundant. Just const is sufficient.

Second. Instead of defining a global array variable for the workspace at the beginning of the module, it would be better to define a constructor function for the workspace, like the following simplified style.

module Foo

workspace(m, n) = Matrix{Float64}(undef, m, n)

function f(ws, x, y)
    ...calculation with workspace ws...
end

end

ws = Foo.workspace(100, 100)
z = Foo.f(ws, 1.2, 3.4)

Always use global variables by passing them as arguments to the function. It is better not to use a global variable in a function without passing it as an argument.

If the size of the workspace is determined by the problem and the algorithm, you can create an object alg describing the algorithm from the object prob describing the problem with alg = SomeAlg(prob), and use the constructor SomeAlg to create a workspace of the required size in alg.

cf. Problem-Algorithm-Solver style coding and Game of Life example

3 Likes

Thank you very much!
In the Problem-Algorithm-Solver style coding
I saw you have things like

using ..Freefall

May I ask, what does the two dots . . between using and Freefall mean?

The manual is a good place to look for those. In this case it’s under “submodules and relative paths”:

https://docs.julialang.org/en/v1/manual/modules/#Submodules-and-relative-paths

1 Like

Not an answer to your question, but if it seems to me that after all your very similar threads of the last days about declaring global variables and their types, you’ve gone back to a very Fortran idiomatic style of doing things. It’s possible of course that in your case this really is the best way to proceed, but it feels a bit more you’re still fighting the language and as a result will have less productive exchanges here, as people here will naturally tend to try and give more idiomatic solutions and not talk about defining constant global variables, restricting their types etc

3 Likes

I was just trying to translate my Fortran code with minimal changes, and without sacrificing performance. So I can have an idea how fast is Julia compared with Fortran.

I notice one thing in Julia perhaps is the most important, that is, type stability.

I believe there are better and smart ways to achieve that as you suggested, but all what I did in defining those global const with Ref is just to lock the type of variables.
So I think by doing this raw and stupid way, I can try my best to maintain type stability.

The way I wrote my Fortran code is that like, my module is really doing a big subroutine.
There are some variables in the module that are ‘global’ inside the module.
In Fortran, these ‘global’ variables in the module does not seem to influence the performance.

Unfortunately, in Julia, they say it is slow to use ‘global’ variables I guess due to ‘thread-safe’ consideration in Julia.
That is why I try to find a way in Julia such that I can still define some const global variables with Ref sometimes, if it does not have negative influence on performance, I feel this is an OK way for me to translate my Fortran code to Julia, because then everything is almost identical.
I do not want to change my code too much right now.

Is this what you want?

Code:

module Foo

const a = Float64[]

function prepare_workspace(m::Integer...)
    resize!(a, prod(m))
    reshape(a, m)
end

prepare_workspace(x::AbstractArray) = prepare_workspace(size(x)...)

function f(x)
    ws = prepare_workspace(x)
    ws .= 2x
end

end

@show Foo.f([1 2; 3 4])
@show Foo.a;

Output:

Foo.f([1 2; 3 4]) = [2.0 4.0; 6.0 8.0]
Foo.a = [2.0, 6.0, 4.0, 8.0]

Remove return nothing.

The idea of defining a constructor makes it simple.

If your Fortran module is in fact a big subroutine, then identify what inputs it must take and make it a Julia function, there’s no need to define a module just for that. A lazy way of doing that may be to put all your mutable globals into a mutable struct, translate all subroutines you have in your module almost literally but passing that struct as an additional argument. Whenever you use a global in Fortran, use the respective field of that struct in Julia. Then, in client code, you may initialize such struct globally and pass it to functions explicitly. The advantage is that, although in a specific user script there might be only one “workspace”, multiple independent ones may be used if needed.

Thing is, avoiding globals is mostly not about performance but about modularity and ability of reason about the programs. In that sense, it’s not about Julia either, you’d get the same advice on using global variables (i.e. “don’t”) from any programming community.

6 Likes