How to define many variables in few lines?

I am very grateful that learned from many guys here that, there is a trick to ‘lock’ the type of a variable by doing a const + Ref trick.
Now, if I have many variables, say 100 variables, how to define them in as few lines as possible?
I mean, do I have to do things like,

const a1 = Ref{Float64}(1.0)
const a2 = Ref{Float64}(1.0)
...
const a100 = Ref{Float64}(1.0)
  1. But that requires 100 lines, is there a way to write them in as few lines as possible?
    I mean, I would like all the Float64 in a block, all the Int64 in a block, all the strings in a block, etc. But anyway, hopefully there is no need to write 100 lines. Because that will make the code somewhat looks like Fortran.

  2. Besides, is there a way, in the above Ref trick, I can leave the value of each const variables as undefined instead of manually give them a value like 1.0?

Thanks in advance!

1 Like

This does not seem like a good idea. Whenever you are generating a bunch of a1, a2, etc. variables, it’s a sure sign that you should be using a vector instead.

Why do you need this?

5 Likes

Thank you!

a1,a2 are just names, they do not need to be ordered and named like vector.
Like it can be any variable. Like in a Fortran code, at the top of a module, I can define things like

integer(kind=i8), private, save :: imode,iter,itermax &
                                  ,msample,mgauss,mgauss_tot,mgauss_max,mgauss_min,nsub,mi,mitot,dim_p,kmix 
real(kind=r8), private, save :: tsum,mu1,sig1,mu2,sig2,muV,SigV,w1,w2,sig,sig_inv,sigsq2_inv,D &
							   ,normpdf_factor_pYq_i,log_normpdf_factor_pYq_i,sigma_factor

If in Julia I need to define them as const … = Ref{…}(…), I need many many lines. But in Fortran it is only two blocks.

Maybe something like this:

(a,b,c,d,e) = (Ref{Float64}(1.0) for _ in 1:5)
4 Likes

Great thank you!
Do I have to give them a value like (1.0)?
Is it possible to do things like (undef) so that I do not need to give them an initial value?

In

(a,b,c,d,e) = (Ref{Float64}(1.0) for _ in 1:5)

it seems I still need to manually count the number of variables. Is there a way to automatically loop over all variables instead of explicitly do 1:5?

Sorry if the questions are stupid.

1 Like

Doesn’t look like it. I don’t normally use Ref, except around broadcasting. Maybe you can use NaN, at least for floats.

It seems like you want to declare them as const, though, and then you shouldn’t go around changing their values.

No, Ref{Float64}() creates an undefined reference but then the compiler has to add a check on each access to it.

You may set a ridiculously large number as the upper limit, Julia will only take as many as there are elements on the left-hand side.

That said, mutable globals is a code smell overall. For immutable globals, a named tuple would be a cleaner syntax:

const GLOBALS = (a = 2, greeting = "Hello", ...)

Reiterating a question from @DNF, are you sure you absolutely need the mutable globals?

6 Likes

You can use metaprogramming:

for i = 1:100
    var_name = Symbol("a", i)
    @eval $var_name = Ref{Float64}(1.0)
end

But I agree with the other posters that this is probably not a good idea.

Edit:
x = Ref{Float64}() initializes x with an undefined (i.e. arbitrary, not reproducable) value. But this is dangerous.
An alternative would be to explicitly use missing: x = Ref{Union{Float64, Missing}}(missing)

2 Likes

Not really sure if I need mutable global actually.
I am a Fortran guy and I am new to Julia.
I am just translating my Fortran code to Julia.

In my Fortran code, I have modules, in the modules I have some variables and arrays defined at the top, which looks like the global stuff in Julia. Like below,

module step
use constants
use mixture
use formats
implicit none 
real(kind=r8), private, allocatable, save :: Yji(:,:),t(:)
integer(kind=i8), private, save :: imode,iter,itermax &
                                  ,msample,mgauss,mgauss_tot,mgauss_max,mgauss_min,nsub,mi,mitot,dim_p,kmix 
real(kind=r8), private, save :: tsum,mu1,sig1,mu2,sig2,muV,SigV,w1,w2,sig,sig_inv,sigsq2_inv,D &
							   ,normpdf_factor_pYq_i,log_normpdf_factor_pYq_i,sigma_factor
real(kind=r8), private, allocatable, save :: LL_iter(:),muk_iter(:,:),sigk_iter(:,:) &
											,muV_iter(:,:),sigV_iter(:,:),wk_iter(:,:),sigma_iter(:) &
											,mukerror_iter(:,:),sigkerror_iter(:,:) &
											,muVerror_iter(:,:),sigVerror_iter(:,:),sigmaerror_iter(:) &
											,ar_gik(:),ar_gik_k(:,:) &
											,thetas(:,:,:),thetaspr(:,:,:) &
											,norm(:),norm_sig(:),normerr(:),log_norm(:) &
											,pYq_ikm(:,:,:),hi_diag(:,:,:,:),yimhi(:,:,:,:) &
											,pYq_km(:,:),log_pYq_km(:,:),h_diag(:,:,:),ymh(:,:,:) &
											,pYq_ikm_o(:,:,:)

real(kind=r8), private, allocatable, save ::wnow(:),log_wnow(:),nik(:,:),nik_sig(:,:),wknik(:,:),wknik_sig(:,:),tauik(:,:) 
integer(kind=i8), private, allocatable, save :: isub_Metroplis_gik_all(:,:),mgauss_ik(:,:)
...
contains

subroutine initialization(...)
blablabla
end 

subroutine A(...)
...
end
subroutine B(...)
...
end
subroutine C(...)
...
end
...
... 
end module step

All those r8 and i8 variables and allocable arrays are stored in the module, and their values can change. So that in my Fortran code, I do not need to put every of them in the arguments of any function.

But it seems in Julia, as many guys pointed out, people don’t do such things.
My Fortran code cannot be translated into Julia in a ‘raw’ way. So I need to think about it. It seems the translation will need to make some change. Otherwise my Julia version code will be way much slower than the Fortran code, and that lost the meaning of my translation.

It is a Monte Carlo Expectation Maximization code with various Metropolis samplings and iterations. Just wanted to see how fast is Julia compared with Fortran.

Thank you so much!
Right, with undef initial values can be dangerous.
It just that, I heard that doing undef perhaps could also have some advantages, such as can detect some bug/error at early stage. If I initialize everthing all using ‘normal’ values, it perhaps will ‘hide’ some bugs.

You don’t need to define the variables like that! Julia does not require variable declaration.

If you do something like this in Fortran:

function f(x,y)
  double precision :: f, x, y, a, b, c
  a = 5.
  b = 3.
  c = 2.
  f = a*x + b*y  + c
end

That in Julia is just

function f(x,y)
  a = 5.
  b = 3.
  c = 2.
  f = a*x + b*y + c
end

By the way, you should not absolutely try to use scalar variables as mutable variables. I mean, doing something like a1 = Ref{Float64}(1.0) is not recommended at all. I understand that you are trying to mimic Fortran behavior, in which one thinks that everything is a mutable variable. That way of thinking must go. Specifically, read this.

12 Likes

Thank you very much.
But sometime it seems I do need to, don’t I? For example,

function step11(it::Int64)
    Muk = Array{Float64,1}(undef,kmix[])
    Sigk = Array{Float64,1}(undef,kmix[])
    error_sigk = Array{Float64,1}(undef,kmix[])
    ar_gik_k_more = Array{Float64,1}(undef,kmix[])

    for k in 1:kmix[]
        Muk(k),error_muk(k),Sigk(k),error_sigk(k),ar_gik_k_more(k) = Metroplis_gik_k_more_o_log(k,msample[])
    end
return nothing
end

I need to define

    Muk = Array{Float64,1}(undef,kmix[])
    Sigk = Array{Float64,1}(undef,kmix[])
    error_sigk = Array{Float64,1}(undef,kmix[])
    ar_gik_k_more = Array{Float64,1}(undef,kmix[])

One by one, because they need to be defined.
Because I want the function Metroplis_gik_k_more_o_log to give values to each elements of the above arrays.
In these cases, it seems defining these arrays in few lines will be helpful.

This might be a case for namedtuples (a=10, b=20, c=30).

There is one thread that discussed some alterntives to that. One is this:

julia> a, b, c = ntuple(i -> Vector{Float64}(undef,0), 3)
(Float64[], Float64[], Float64[])

julia> a
Float64[]

julia> b
Float64[]

julia> c
Float64[]

By the way: Vector{Float64} is an alias for Array{Float64,1}. And I’m not sure what you are aiming there, but a vector of kmix[] does not sound right. What do you want to create there?

1 Like

Thanks.
kmix is defined as

 const kmix = Ref{Int64}()

So I need use kmix[ ] to get the value.

Do you think in the function step11, it is better to specify a known-size array Vector{Float64}(undef,kmix[ ]), or simply an unknown-size array Vector{Float64}(undef,0) ?
I feel specify the size Vector{Float64}(undef,kmix[ ]) is better than just do Vector{Float64}(undef,0). Because the computer then knows how much memory to be allocated to the Vector{Float64}(undef,kmix[ ]).
I feel Vector{Float64}(undef,0) is not enough, the explicit size is needed.

const is for defining actual constants. If you don’t know the value of kmix and will in fact change it at runtime it’s better to leave it as an ordinary variable, and avoid globals that aren’t true constants.

Can’t you make this into

function step11(it, kmix)
    Muk = Array{Float64,1}(undef, kmix)
    ...

?

BTW this function is confusing. It has one input, which it does not use, and apparently accesses two global variables instead of giving them as input arguments.

Then it creates several arrays, but returns nothing, so those arrays will disappear. It seems to rely heavily on ‘spooky action at a distance’, which, imho, should be avoided. Is this considered idiomatic in Fortran?

2 Likes

Everything that @DNF said plus that Muk(k),error_muk(k),Sigk(k),error_sigk(k),ar_gik_k_more(k) isn’t Julia either. We access arrays with square brackets.

Perhaps you shouldn’t copy-paste translate your Fortran code but instead think of its overall structure and dependencies and then think about how one would model this structure in Julia. Afterwards you can fill this “construct” with your real code, i.e. variable names, operations, etc.

1 Like

Thanks.
Of course I can make

step11(it, kmix)

But that is not the point.
I use Ref, and [ ] symbol in kimx[ ] simply to remind me that it is a global variable and its type is locked.

The step11 function is just a small piece for illustration, all the arrays created will be used of course.

For me, modern Fortran’s grammar seem to be more easy to make sure type stability and easy to write fast code.

In Julia it seems more easy to make such a mistake (Please correct me if I am wrong)

function f(a::Int64)
  a=5.1 
  println(a) 
end

Here a should be Int64, but can be accidently changed to Float64 without warning.

But the point is that you shouldn’t use global mutable variables.

1 Like

First of all, it very much is the point. Using global variables in that way is highly unidiomatic and will likely get you into troubles in some way (unless, maybe, if you know exactly what you’re doing). Generally speaking, unless you’re “forced to”, don’t use mutable global variables.

Well, at the risk of sounding a bit harsh (sorry in advance), why don’t you write Fortran then? Why do you want to use Julia in the first place if you like Fortrans grammar, speed etc.? Learning a new languange means learning a new language. And not just taking everything you know from a different language and one-to-one translating it.

2 Likes