Consider the following data and two macros:
data mydata;
input a b;
datalines;
1 2
3 4
;
run;
%macro m1;
data _null_;
set mydata;
if a=1 then call symput('x1', put(b, 1.));
run;
%mend m1;
%macro m2(n=);
data _null_;
set mydata;
if a=1 then call symput('x2', put(b, 1.));
run;
%mend m2;
options mprint;
%m1;
%m2(n=1);
%put &x1;
%put &x2;
When I run this code, %put &x1 prints 2 to the log, as I expected. However, with the second macro--in which the only difference from the first is that it takes in a parameter (which isn't even used in the above example)--the result is instead a warning about the apparent symbolic reference not being resolved. I found a solution to get around this, namely adding %global x2 to the beginning of the macro. But my question is: Can anyone explain why macro m1 works fine, but macro m2 doesn't work unless the %global statement is added?
I don't think either work as expected. Run both with a clean session and you should get the error overall.
I would recommend using CALL SYMPUTX which has a third parameter and allows you to create a global macro variable.
Let me second @Reeza's recommendation. Since SYMPUTX came along, I never use SYMPUT. Furthermore, SYMPUTX automatically does numeric to character conversions.
Will this help?
Yeah, I think 1. is getting at the answer. Also just found this paper (http://www.lexjansen.com/phuse/2009/po/PO15.pdf) which seems to explain it:
"CALL SYMPUT may not create a GLOBAL macro variable when defined inside a macro definition. When we use CALL SYMPUT inside a macro definition, it will write the macro variable values into a local symbol table, if the macro has a non empty local symbol table. If macro has an empty local symbol table, the macro variable defined inside a macro, using CALL SYMPUT routine has global scope. The rules do not apply when you explicitly define them as GLOBAL or LOCAL."
Yes Sir, basically how I remember is all macro variables are created or overwritten(values) in the closest non empty symbol table unless there is a global macrovar with same name. Either way, when inside a macro wrapper as in your case, the parameter definition in itself makes your local symbol table non empty. Therefore, with parameter n= created a local macro var with a null value meaning 0 characters, however it is a still a valid value. When your call symput executes, the macro processor identifies the presence of the non empty local symbol table with N in it. Therefore, the macro var created by call symput goes straight to local symbol table of your second macro wrapper. I hope this explains a touch better. Cheers!
@novinosrin wrote:
Yes Sir, basically how I remember is all macro variables are created or overwritten(values) in the closest non empty symbol table unless there is a global macrovar with same name. Either way, when inside a macro wrapper as in your case, the parameter definition in itself makes your local symbol table non empty. Therefore, with parameter n= created a local macro var with a null value meaning 0 characters, however it is a still a valid value. When your call symput executes, the macro processor identifies the presence of the non empty local symbol table with N in it. Therefore, the macro var created by call symput goes straight to local symbol table of your second macro wrapper. I hope this explains a touch better. Cheers!
I like this explanation the best, except for one thing. This:
unless there is a global macrovar with same name
should be rephrase to be something more like:
unless there is a EXISTING macrovar with the same name
Basically CALL SYMPUT() or %LET will use an existing macro variable before it creates a new macro variable. It doesn't matter if it is global or if it exists in the scope of another macro that is running. So if you make a local macro variable called FRED and call a subroutine macro that updates FRED without defining it as local it will update your local macro variable.
That's interesting @novinosrin. I realize you copied the explanation from the docs, but regarding the three situations where SYMPUT will write to local symbol table, "even if it's empty":
@Quentin Sir, Briiliant and Great catch!. Hmm, will have to dig in seriously to have an explanation for the computed goto.
As the definition says, A text expression that generates a label in a %GOTO statement is called a computed %GOTO destination.
Here are my crazy guesses-
1. May be the & or % trigger in the expression is a nested macro call that at compile time made the local symbol table non empty.
2. The text expression without a macro trigger prolly wouldn't be relevant to affect the symbol table
3. So the correlation perhaps would have to be %Goto has to be in macro wrapper, and the label that goes with it viz. if is a result of macro processor resolution hitting the SASMacr catalog or symbol table
Thank you for waking me!
Do bear in mind that creating global macro variables in levels of macro down can lead to some really hard to debug, obfuscated code. There is never a need to do such a thing. Global macro varaibles are just that, macro varaibles available to the whole session and thus should be setup at session start. Creating global macros within code shows a distinct lack of planning, and poor design.
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.