While playing around with loops in data sets and assinging macro variables with SYMPUT, I stumbled accross an issue, where SAS doesn't do what I would expect it to do.
I want to use a loop within a data set and assign the incrementer to a macro variable. I personally never believed this could be an issue. However, SAS ...
A simple example is as follows:
[CODE1]
%let x = 1; data _NULL_; do i = 1 to 5; call symput('x', i); put "x now is &x. and i is " i; end; run;
[OUTPUT1]
x now is 1 and i is 1
x now is 1 and i is 2
x now is 1 and i is 3
x now is 1 and i is 4
x now is 1 and i is 5
Am I so **bleep** stupid to assume that the following output would be reasonable?:
[OUTPUT1*]
x now is 1 and i is 1
x now is 2 and i is 2
x now is 3 and i is 3
x now is 4 and i is 4
x now is 5 and i is 5
OK. I was then thinking that maybe the %let statement at the beginning somehow overrules everything and I would have to remove it. So, I did that:
[CODE2]
data _NULL_; do i = 1 to 5; call symput('x', i); put "x now is &x. and i is " i; end; run;
What would you expect now?
Don't tell me you would expect this:
[OUTPUT2]
x now is 5 and i is 1
x now is 5 and i is 2
x now is 5 and i is 3
x now is 5 and i is 4
x now is 5 and i is 5
The different space, 5, .... I mean, I cannot even imagine why they made it that way... 🙂
Can someone please help me?
The software needs to compile the DATA step, then run it. That means the contents of the PUT statement do not change as the DATA step executes. The value of &X before the DATA step begins is the one that is used within the PUT statement.
The second DATA step says that &X is 5, because you ran the steps as part of the same program. As the first DATA step begins, &X is 1. So the PUT messages reflect that. Once the first DATA step is over, &X is 5. So when the second DATA step gets compiled, that's the value that becomes part of the PUT statement.
The software needs to compile the DATA step, then run it. That means the contents of the PUT statement do not change as the DATA step executes. The value of &X before the DATA step begins is the one that is used within the PUT statement.
The second DATA step says that &X is 5, because you ran the steps as part of the same program. As the first DATA step begins, &X is 1. So the PUT messages reflect that. Once the first DATA step is over, &X is 5. So when the second DATA step gets compiled, that's the value that becomes part of the PUT statement.
Hi Andreas,
call symput only writes to the macro symbol table once the data step finishes processing. If you trace through your code execution with that knowledge, you'll find that the behaviour you're seeing is valid and predictable.
Cheers,
Andy.
I think a segment of the documenation for CALL SYMPUT helps explain what you're seeing:
"Problem: Trying to Reference a SYMPUT-Assigned Value Before It Is Available
One of the most common problems in using SYMPUT is trying to reference a macro variable value assigned by SYMPUT before that variable is created. The failure generally occurs because the statement referencing the macro variable compiles before execution of the CALL SYMPUT statement that assigns the variable's value. The most important fact to remember in using SYMPUT is that it assigns the value of the macro variable during program execution. Macro variable references resolve during the compilation of a step, a global statement used outside a step, or an SCL program. As a result:
You cannot use a macro variable reference to retrieve the value of a macro variable in the same program (or step) in which SYMPUT creates that macro variable and assigns it a value.
You must specify a step boundary statement to force the DATA step to execute before referencing a value in a global statement following the program (for example, a TITLE statement). The boundary could be a RUN statement or another DATA or PROC statement."
In your example the following steps take place:
1. You set x = 1 with your %let.
2. You run your data loop to set x to the value of the incrementer (why you want to do this is not clear...). This value does not get set to the symbol table until your code has run, however, per the above documentation, so your "put" statement shows the value from the %let.
3. One the data step runs, x is now set the last value of the incrementer (5). Note, however, that since it was set with call symput and not %let, it has trailing blanks. You could fix this with call symputx.
4. When you run the next data step, the same thing is happening, except the current value of x is what you last created in the previous data _null_.
So from my perspective, it's doing what it's supposed to do according to how the software works. Maybe a more important question is what are you trying to do?
Just for amusement!
Tom
%let x = 1;
data _NULL_;
do i = 1 to 5;
call symput('x', i);
y = symget('x');
put "x now is &x. and i is " i " and y is " y;
end;
run;
x now is 1 and i is 1 and y is 1
x now is 1 and i is 2 and y is 2
x now is 1 and i is 3 and y is 3
x now is 1 and i is 4 and y is 4
x now is 1 and i is 5 and y is 5
Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.
Register today!What’s the difference between SAS Enterprise Guide and SAS Studio? How are they similar? Just ask SAS’ Danny Modlin.
Find more tutorials on the SAS Users YouTube channel.
Select SAS Training centers are offering in-person courses. View upcoming courses for: