This question comes purely out of intellectual curiosity.
Having browsed the Python section relatively often, I've seen a number of questions similar to this, where someone is asking for a programmatic way to define global variables. Some of them are aware of the pitfalls of exec
, others aren't.
However, I've recently been programming in Stata, where the following is common:
local N = 100
local i = 1
foreach x of varlist x1 - x`N' {
local `x' = `i' * `i'
++i
}
In Stata parlance, a local macro with the name N
is created, and N
evaluates to 100. In each iteration of the foreach
loop, a value from x1
to x100
is assigned to the local macro x
. Then, the line inside the loop, assigns the square of i
to the expansion of x
, a local macro with the same ending as i
. That is, after this loop x4
expands to 4^2 and x88
expands to 88^2.
In Python, the way to do something similar would be:
squares = {}
for x in range(1,101):
squares[x] = x**2
Then squares[7]
equals 7^2.
This is a pretty simple example. There are a lot of other uses for stata macros. You can use them as a way to pass functions to be evaluated, for example:
local r1 "regress"
local r2 "corr"
foreach r of varlist r1-r2 {
``r'' y x
}
The double tickmarks around r
expand that macro twice, first to r1
/r2
then to regress
/corr
, with the result of running a linear regression with y
as the dependent and x
as the independent variable, and then showing the correlation between y
and x
. Even more complex stuff is possible.
My question is basically, does stata fall into some larger category of languages where variable assignment/evaluation takes this form of "macro assignment/expansion"? Bonus points for any explanation of why a language would be designed like this, and/or examples of similar constructs in other languages.
It's really just a question of how much syntactic sugar is there. In any language worth its salt, you can use a map or dictionary data structure to create variable names (keys) at runtime with some value. Some languages may more transparently integrate that with ordinary variable identifiers than others.
(Sorry this is an "answer," not a comment.... people don't rate my answers, so I don't have enough points to comment on the question.)
First, let me point out what that is strange about Stata is that it translates the macro before execulting that line of code. For example:
Say you type.
local x3 = 20
local y = 3
display "I am `x`y'' years old"
Internally, Stata is going to translate the locals (inner to outer) and then execute the display
command.
That is, Stata will translate the command
display "I am `x3' years old"
then
display "I am 20 years old"
then, Stata will actually execute this last line of code. You can watch all of this by first executing this command: set trace on
.
There is a subtle difference. The ` '
brackets change the command. I think this is actually different than other programing languages. You can often do something in Stata with one line of code where other languages would require two lines of code (one to "expand" the macro; one to execute the line of code).
What's useful about this is that Stata can also evaluate all kinds of expressions inside the ` '
brackets (as long as they return a number or string... e.g., nothing that returns a matrix)
display "I am `= 2011 - 1991' years old"
display "I am `= floor(uniform()*`x`y'')' years old"
This is immensely useful once you get used to it. Macros make things in Stata way more clean than you would do in, for example, SAS. SAS's %let
statement's aren't nearly as flexible.
I was also going to point out a few mistakes.
(1) The loop is set up wrong in these examples. x1, x2, ... , x100
are macros (locals), not variables. You can't say foreach x of varlist x1 - x100
because x1-x100
is not a variable list. If I was trying to do that, I would probably use:
local N = 100
forvalues i = 1/`N' {
local x`i' = `i' * `i'
}
The same mistake is made in the second example. r1
and r2
are not variables.
You could do this:
local mycommands regress corr
foreach r in `mycommands' {
`r' y x
}
(Although I would actually type the equivalent, foreach r of local mycommands { ... }
, which is supposedly a hair faster to execute).
(2) Second, ++i
is not valid. You probably meant to say local ++i
.
I don't know if this is what you are looking for, but in PHP you can do:
for ($i=0; $i<10; $i++) {
${x.$i} = $i*$i;
}
print $x3; // prints 9
print $x4; // prints 16
I personally find this very unpleasant.
Javascript is an obvious example, although the mechanism is like Python, not Stata.
for(var i = 0; i < 100; i++)
this["var" + i] = i * i;
alert(var8); // 64