Best way to create/assign variables based on strings

Hi all

Which would be best way to create/assign variables based on strings. Assuming I have this:

string = “AA”
string = get(“https://get.some.unknown.very.short.string”)

would like to do the following

AA = 1
new_variable_named_on_that_yet_unknown_short_string = 1

Thank you!

Sorry not answering your question, but consider using JSON.jl.

Use a dictionary: http://docs.julialang.org/en/stable/stdlib/collections/#Base.Dict

4 Likes

If this is in global scope. Use eval. If this is local scope, it’s impossible and won’t be possible. Use a Dict with String as keys to hold the value instead.

3 Likes

Here is the code, but this is not the best practice:

julia> string = "AA"
"AA"

julia> value = 1
1

julia> eval(parse("$string=$value"))
1

julia> AA
1

julia> 

Even if you want to use eval. DO NOT USE parse.

Do @eval $(Symbol(string)) = $value or @eval $(Symbol(string)) = value.

4 Likes

Thank you for all the answers.

[quote=“yuyichao, post:4, topic:3001, full:true”]
If this is in global scope. Use eval. If this is local scope, it’s impossible and won’t be possible. Use a Dict with String as keys to hold the value instead.
[/quote]
Found a “solution” in local scope by widening the field of permissible functions. In my setting “AA” and “BB” represent those short-strings yet to be fetched
var1 = "AA"
var2 = "BB"

but on the contrary, x and y are known at coding time

x = 2.0
y = rand(2,2)

An ugly solution may be:

function crazymeta(file)
open(file, "w") do f
write(f,"$var1 = x\n$var2 = y")
end
include("$file")
return AA * BB
end

CC = crazymeta("whatever.jl")
print("$CC")

Now AA = 2.0, BB and CC are naturally 2x2 Array{Float64,2}. Would it possible to do something like this print/include trick, but with all steps in memory and reasonably fast?

Well you could use include_string, but include operates at global scope.
In your example, AA and BB will be defined as global variables.
In this case, probably best to go with @yuyichao’s suggestion and use @eval.

A better way to write this is

var1 = "AA"
var2 = "BB"
@eval $(Symbol(var1)) = x
@eval $(Symbol(var2)) = x
CC = AA * BB
print(CC)

but again, this is not usually the best way to go about things. What are you trying to do? By that I mean your actual goal, for which this metaprogramming may likely not be the best solution for.

I agree that using a Dict is better in most cases. However, by defining a macro it is certainly not impossible to create variables in local scope only. I posted one possible solution in another thread. Or did I misunderstand what you meant?

No it’s impossible to create a variable in local scope from runtime value. You can of course use a macro to translate @m "b" into b = 1 but it’s impossible to make var = "b"; @m var behave like b = 1.

2 Likes

In another word, if there’s a way to do this (create local variable from runtime value), it’s a semantic bug that needs to be fixed.

1 Like

I see. I should have read the original post more carefullly instead of jumping all over my example in the other thread. Thanks for clarifying!

Note that the macro is very unlikely the right solution in the other thread either. It essentially let you spell variable syntax slight differently, like writing "a" = 1 instead of a = 1, which might be cool if you really like quoting but I don’t think that’s what the OP asked about in either thread.

1 Like

Sorry for nagging, but there is obviously something I still don’t understand. The macro in the other thread seems to work (the function creates a random runtime value and the macro creates a variable with that value in the local scope). Here’s the code:

macro localvariable(s::AbstractString, v::Any)
	s = Symbol(s)
	esc(:($s = $v))
end

function newtest()
    c = rand()  
    @localvariable("b", c)
    println("b in function = $b")
end

newtest()
isdefined(:b) && println("global b = $b")
nothing

Output:

b in function = 0.8428874752942634

What am I missing?

What you are missing is that the macro isn’t useful other than making it possible to do things like

It does NOT create a variable whose name is determined at runtime, merely changing how you spell assignments. Also, it’s missing esc.

4 Likes

I have a use case for which I could probably use strings as variable names. I want to construct a graph with some vertices. Since I want to create them iteratively, I use a for loop here but this doesn’t work as expected.

for i = 1:max_nodes
  @eval $(Symbol("V$i")) = ExVertex(i, "V$i")
end

So, I switched back to your primary suggestion of using Dictionaries. So far, they work fine.


vertices = Dict("V$i" => ExVertex(i, "V$i") for i=1:max_nodes)
1 Like

I am replying here to the person who said never to use “parse”.Please excuse if I’ve put my posting in a wrong place, and move it if you can. If yes, I apologize for not being good at social media.
This response is human, not technical, from an old Assembler Language programmer who even keypunched his own COBOL programs and also wrote supervisor mode Operating System code (MVS) in IBM. Don’t tell persons who have been thru the mill what they cannot do because they’ve had far too much of that from IBM’s (Idiots who Became Manages) and other people who were part of the problem, not part of the solution. If you tell me not to do something, I will want to do it; tell me to do it and I’ll want not to do it. I am a disabled veteran of the Great GOTO War, which left a very bad taste in my mouth. If you tell me that “parse” may not be supported in a future release, I’m all ears to listen to you, because I’ve been burnt by that kind of stuff, too. But “thou shalt not” just reminds me of the True Believers in “Improved Programing Technologies” and my intrusive parents. Respectfully.

3 Likes

@Julialang261, you should tell us more about the interesting “Great GOTO War”.
What about considering a post in the Meta Discussion category? Maybe it would deserve a dedicated “Computing Histories” tag.
:floppy_disk:

While @yuyichao could have given an explanation at the time, instead of just a negative and an alternative, the reason is simple: you are doing a lot of unnecessary work by parsing from a String to get the expression object to be evaluated, using @eval and interpolation allows you to build the expression object directly, without a parsing phase.

3 Likes