BookmarkSubscribeRSS Feed
derekg
Obsidian | Level 7

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?

9 REPLIES 9
Reeza
Super User

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 SYMPUTwhich has a third parameter and allows you to create a global macro variable. 

WarrenKuhfeld
Rhodochrosite | Level 12

Let me second @Reeza's recommendation.  Since SYMPUTX came along, I never use SYMPUT.  Furthermore, SYMPUTX automatically does numeric to character conversions.

novinosrin
Tourmaline | Level 20

Will this help?

 

Two rules control where CALL SYMPUT creates its variables:
  1. CALL SYMPUT creates the macro variable in the current symbol table available while the DATA step is executing, provided that symbol table is not empty. If it is empty (contains no local macro variables), usually CALL SYMPUT creates the variable in the closest nonempty symbol table.
  2. However, there are three cases where CALL SYMPUT creates the variable in the local symbol table, even if that symbol table is empty:
    • Beginning with SAS Version 8, if CALL SYMPUT is used after a PROC SQL, the variable will be created in a local symbol table.
    • If the macro variable SYSPBUFF is created at macro invocation time, the variable will be created in the local symbol table.
    • If the executing macro contains a computed %GOTO statement, the variable will be created in the local symbol table. A computed %GOTO statement is one that uses a label that contains an & or a % in it. That is, a computed %GOTO statement contains a macro variable reference or a macro call that produces a text expression. Here is an example of a computed %GOTO statement:
derekg
Obsidian | Level 7

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."

novinosrin
Tourmaline | Level 20

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!

Tom
Super User Tom
Super User

@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.

 

Quentin
Super User

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":

  1. Beginning with SAS Version 8, if CALL SYMPUT is used after a PROC SQL, the variable will be created in a local symbol table.
  2. If the macro variable SYSPBUFF is created at macro invocation time, the variable will be created in the local symbol table.
  3. If the executing macro contains a computed %GOTO statement, the variable will be created in the local symbol table. A computed %GOTO statement is one that uses a label that contains an & or a % in it. That is, a computed %GOTO statement contains a macro variable reference or a macro call that produces a text expression.
     
    It seems to me that #1 is not an exception.  When you run PROC SQL, it creates (local) macro variables SQLOBS etc.  So the local symbol table is not really empty after that.  Similarly #2 is not an exception.  When you define a macro with /parmbuff it creates an automatic macro variable SYSPBUFF in the local symbol table, so it's not empty.
     
    I have no idea why a computed %GOTO would change the behavior of CALL SYMPUT (as it does).  I would suspect that there may be some hidden (?) macro variable created in the local symbol table, for some reason.  Any thoughts as to a better explanation?
     
     
     
     
     
BASUG is hosting free webinars Next up: Mike Sale presenting Data Warehousing with SAS April 10 at noon ET. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
novinosrin
Tourmaline | Level 20

@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!

 

 

 

RW9
Diamond | Level 26 RW9
Diamond | Level 26

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-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

Register now!

How to Concatenate Values

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 9 replies
  • 2035 views
  • 6 likes
  • 7 in conversation