How to use string substitution in variable names

While I agree that this is perhaps not an appropriate place for using meta-programming, I beleive it’s still useful to also answer the question that was asked. Since the only answer here that attempts to address the actual question asked uses eval, I want to note that something like this can be done very cleanly without eval, and is actually an important part of julia’s Cartesian indexing implementation.

The way to do this when the number of unit vectors needed is known at macroexpansion time is to write a macro. Here’s one possible way:

julia> macro define_basis_vecs(sym::Symbol, n::Integer)
           Expr(:block, __source__, map(i -> :($(esc(Symbol(sym, i))) = $( [j==i for j in 1:n] )), 1:n)...)
       end
@define_basis_vecs (macro with 1 method)

We can macroexpand it to make sure it generates the right code:

julia> @macroexpand @define_basis_vecs e_ 5
quote
    #= REPL[15]:1 =#
    e_1 = Bool[1, 0, 0, 0, 0]
    e_2 = Bool[0, 1, 0, 0, 0]
    e_3 = Bool[0, 0, 1, 0, 0]
    e_4 = Bool[0, 0, 0, 1, 0]
    e_5 = Bool[0, 0, 0, 0, 1]
end

Unlike solutions with eval, this will work in the local scope.

julia> let 
           @define_basis_vecs e_ 7
           e_1 * e_7'
       end
7×7 BitMatrix:
 0  0  0  0  0  0  1
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0

and won’t leak any variables out of that scope:

julia> e_1
ERROR: UndefVarError: e_1 not defined

But again, as was pointed out earlier, this is a usecase that is usually better satisfied in much more ‘boring’ ways without any metaprogramming.

If you’ve spoken to your doctor and you think that Metaprogramming may be right for you, I’d recommend reading this section of the manual thoroughly: Metaprogramming · The Julia Language

12 Likes