BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
dxiao2017
Lapis Lazuli | Level 10

This is the results:

no %put in %macro test2 (got a error message), has %local statement in %macro test3 (nothing changed the result is 2):

dxiao2017_0-1741452597441.pngdxiao2017_1-1741452716116.png

has %put in macro test2 (no error message); has %local in macro test3 (nothing changed the result is 2);

dxiao2017_2-1741452923300.png

thanks a lot for all your comments, Tom, but are you sure about your comments, or you just want me to try something out for you, why is %local statement is so important to you and need to bring it up, I do not understand

Tom
Super User Tom
Super User

Your examples have obviously incorrect code.

For example why did you remove just the %PUT from a statement like:

%put &i;

Leaving just the statement

&i;

When I said the you did not need to wrap the %PUT statement inside of macro definition to test whether calling a macro had changed the macro variables value?

%let i=Before macro call;
%test1;
%put &=i;

Here is simplified example of a problem that I have seen users make hundreds of times:

Say you had a macro that uses a %DO loop.  Here is a simple example:

%macro inner(list);
%do i=1 %to %sysfunc(countw(&list,%str( )));
  %let item=%scan(&list,&i,%str( ));
  %put item &i has value &item..;
%end;
%mend;
%inner(a b c);

Now you are working on a larger problem and what to create a macro that will call that macro in a loop.

Here is a simple example:

macro outer(list);
%do i=1 %to %sysfunc(countw(&list,|));
  %let sublist=%scan(&list,&i,|);
  %put sublist &i has values &sublist..;
  %inner(&sublist)
%end;
%mend;

What would you expect to print if I made this macro call?

%outer(a b c|1 2 3|x y)

Result:

Spoiler
 86         %outer(a b c|1 2 3|x y)
 sublist 1 has values a b c.
 item 1 has value a.
 item 2 has value b.
 item 3 has value c.

Can you explain why the result is not what you wanted? What happened to 1 2 3 and x y ?

Now if you just fix the %INNER macro by defining the macro variables it uses a %LOCAL

Try it your self and compare to these results:

Spoiler
71         %macro inner(list);
 72         %local i item ;
 73         %do i=1 %to %sysfunc(countw(&list,%str( )));
 74           %let item=%scan(&list,&i,%str( ));
 75           %put item &i has value &item..;
 76         %end;
 77         %mend;
 78         %outer(a b c|1 2 3|x y)
 sublist 1 has values a b c.
 item 1 has value a.
 item 2 has value b.
 item 3 has value c.
 sublist 2 has values 1 2 3.
 item 1 has value 1.
 item 2 has value 2.
 item 3 has value 3.
 sublist 3 has values x y.
 item 1 has value x.
 item 2 has value y.

 

dxiao2017
Lapis Lazuli | Level 10

Hi Tom thanks a lot for your further suggestions and instructions, however I think I have not get to there yet, too much difficulty (and a little bit too advanced) for me at the moment. Obviously I cannot even understand what does the call keyword in the %let i=Before macro call; statement means (I'v just start to learn macro for a very short time btw), not to mention the examples in the spoiler. It could take me hours to research, understand, and try your examples. Many thanks for your feedback and I will for sure try out the examples later (when I need much less time to figure out those examples). Thanks very much again!

Tom
Super User Tom
Super User

CALL is a common verb used when discussing programs.  You CALL a function or a subroutine or a method or a macro or a whatever name the language you are discussing uses for something that users need to INVOKE for it begin doing its job.

dxiao2017
Lapis Lazuli | Level 10

Is this (see below) what you and Tom want me to try? Without the %local statement: got a warning message (&count cannot resolve) and something massed up with the report title(the 2011 one becomes 2014, which is not correct):

%macro storms(list);
*%local count i yr;
%let count=%sysfunc(countw(&list));
%do i=1 %to &count;
   %let yr=%scan(&list,&i);
   title "&yr Storms";
   proc means data=mc1.storm_final n min 
                    mean max maxdec=0;
      var MaxWindMPH MinPressure;
      where season=&yr;
   run;
%end;
%mend storms;
%storms(2011 2012 2014);

%let count=100;
%storms(2011 2012 2014);
%put &count;

%symdel count;
%storms(2011 2012 2014);
%put &count;

dxiao2017_0-1741448457630.pngdxiao2017_1-1741448589646.png

dxiao2017_2-1741448710780.pngdxiao2017_3-1741448808311.png

dxiao2017_4-1741449194442.png

Quentin
Super User

@dxiao2017 wrote:

Is this (see below) what you and Tom want me to try? Without the %local statement: got a warning message (&count cannot resolve) and something massed up with the report title(the 2011 one becomes 2014, which is not correct):

%macro storms(list);
*%local count i yr;
%let count=%sysfunc(countw(&list));
%do i=1 %to &count;
   %let yr=%scan(&list,&i);
   title "&yr Storms";
   proc means data=mc1.storm_final n min 
                    mean max maxdec=0;
      var MaxWindMPH MinPressure;
      where season=&yr;
   run;
%end;
%mend storms;
%storms(2011 2012 2014);

 

There is a small mistake in that example you wrote.  You tried to comment out the %LOCALstatement with:

*%local count i yr;

But the * is not a comment in the macro language.  That %LOCAL statement will still be executed.  

 

To comment it out, you need a macro comment, either %* or /* */ .   So you could comment it out with:

%*local count i yr;

I'll write more in another post.

dxiao2017
Lapis Lazuli | Level 10
Thanks a lot, Quentin and Tom! this is too much information, will again try it later, thanks for all these comments!
dxiao2017
Lapis Lazuli | Level 10

with the %local statement: there is still a warning message (because the global macro &count which had the value of 100 was deleted), but the results are good (titles were correct): I still do not get why is the %local statement necessary here in the macro storms, btw, because even without it the storms macro runs correctly all the time and after adding it some mass was produced

%macro storms(list);
%local count i yr;
%let count=%sysfunc(countw(&list));
%do i=1 %to &count;
%let yr=%scan(&list,&i);
title "&yr Storms";
proc means data=mc1.storm_final n min
mean max maxdec=0;
var MaxWindMPH MinPressure;
where season=&yr;
run;
%end;
%mend storms;
%storms(2011 2012 2014);

%let count=100;
%storms(2011 2012 2014);
%put &count;

%symdel count;
%storms(2011 2012 2014);
%put &count;

 

dxiao2017_0-1741450024482.pngdxiao2017_1-1741450116093.png

dxiao2017_2-1741450509218.png

Quentin
Super User

The issue of macro variable scope is a tricky one, and worth working through.  

 

You asked for a brief explanation why the %LOCAL statement should almost always be used to define macro variables as %local to a macro.  I would say:

 

  • Suppose I'm a macro user.  And in my SAS session I have some global macro variables that I created for my own work.
  • I call a macro, %storms() .
  • I don't expect the macro storms to change the value of any of my global macro variables, they belong to me, not the macro.
  • If the author of the the macro %storms() did not use the %local statement to define macro variables as %local, then the macro storms might change the value of my global macro variables.  And as a user of the macro, there is no way for me to prevent that from happening.

 

The issue is not necessarily whether the macro itself works, it's instead a side-effect problem of the macro changing something that doesn't belong to the macro.

 

Consider this macro without a local statement, which just writes the items in a list to the log:

%macro withoutlocal(list);
  %let count=%sysfunc(countw(&list));
  %do i=1 %to &count;
     %let yr=%scan(&list,&i);
     %put &=yr ;
  %end;
  %put _user_ ;
%mend withoutlocal;

If I call that macro:

%withoutlocal(A B C)

it works.  And the macro variables COUNT, I, and YR will all be created as local macro variables.  I added a %PUT _USER_ statement to show this:

197  %withoutlocal(A B C)
YR=A
YR=B
YR=C
WITHOUTLOCAL COUNT 3
WITHOUTLOCAL I 4
WITHOUTLOCAL LIST A B C
WITHOUTLOCAL YR C

However, suppose I create a global macro variable named count, which I want to use in my program for some purpose unrelated to the macro.  And then suppose I call the macro.

%let count=100;
%withoutlocal(A B C)
%put &=count;

Now the log shows that instead of creating a local macro variable named COUNT, the macro used *my* global macro variable count.  The macro "worked" in terms of returning the items of the list.  But after the macro has run, my macro variable COUNT has the wrong value, because the macro wrote the value 3 to my macro variable, instead of creating its own macro variable:

198  %let count=100;
199  %withoutlocal(A B C)
YR=A
YR=B
YR=C
WITHOUTLOCAL I 4
WITHOUTLOCAL LIST A B C
WITHOUTLOCAL YR C
GLOBAL COUNT 3
200  %put &=count;
COUNT=3

As a user of a macro, I can't prevent the macro from accidentally changing my macro variables, if I happen to have macro variables that have the same name as macro variables used in the macro.  I rely on the macro author to be conscientious enough to declare all the macro variables to be %local, to prevent "collisions" with my macro variables.

 

The general guideline for writing macro is when you create a macro variable, always use the %LOCAL statement to create them as local to the macro, unless you have a really really good reason not to do so.  To protect your users (and yourself) from collisions.

dxiao2017
Lapis Lazuli | Level 10
Thanks a lot Quentin, this is clearer and helpful, nevertheless it still takes me some more time to figure out myself, I'll try it later, thanks!
dxiao2017
Lapis Lazuli | Level 10

Hi Tom, (I do not know if I understand your comments correctly, it looks like the %local statement is for retaining a macro variable value only for the current use and prevent it from overwriting a previously created macro variable value that has the same macro name, am I right or wrong?) the material did not left out the %local statement and has section and questions for it, I have not get to there yet, because I personally would like to learn write a simple and useful macro (according to what I would like to produce) first, and then learn storing, re-using macros, and etc. alike contents, and thus I omitted the several questions about the %local and %global statements at the moment and will learn it later.

Tom
Super User Tom
Super User

You are close.

 

You are right that for this example macro it does not impact how this macro behaves.  You demonstrated that in your testing.  The macro can still assign values to the macro variables it is using and retrieve the values it has assigned.  

 

Where it does make a difference is for the environments (open code or some other running macros) that might call this macro.

 

This meaning of LOCAL is different than what you will find in most other computer languages that allow local variables.  In those languages LOCAL is meant to protect your variables from being seen and/or modified by other programs.  But in the SAS macro processor language you use LOCAL macro variables to prevent your program (the currently running macro) from modifying macro variables defined by other macros (or global macro variables).

 

The %LOCAL statement will create a macro variable in the symbol table of the current macro (if one already exists then it does nothing).  Which symbol table the macro is in determines its "scope".  You can see all macro variables that exist in the scope of macros that have called the current macro.  Except when they are hidden by macro variables with the same name in a "lower" scope (that is closer to the scope of the currently executing macro).    

 

You can also modify those macro variables that live (AKA are defined  in; AKA  LOCAL to) macros that have called you and global macros.  The exception being some system/automatic macro variables that are readonly.  And recently SAS added syntax that make it is possible for users to define readonly global macro variables.

 

When you reference a macro variable using &mvarname  SAS will use the macro variable it finds in the lowest scope.   

 

When you  assign a value to a macro variable using %LET mvarname= SAS will use an existing macro variable before making a new one. When it does make a new one it is make  in the current scope.  Note actually when the current macro has not defined any macro variables then CALL SYMPUT() will create new macro variables in the lowest scope that does have macro variables.  See discussion in CALL SYMPUT() documentation.

 

Note that parameters that the macro accepts (defined in the %MACRO statement) are always defined as LOCAL macro variables in the macro's scope.

 

And if you really want to find the value of macro variable that has been blocked/hidden by a macro variable with the same name at a lower scope you can use this macro:  https://github.com/sasutils/macros/blob/master/symget.sas

 

dxiao2017
Lapis Lazuli | Level 10
This is too much information, could you summarize the usage of %local statement for example using no more than 4 simple bullet points, thanks a lot
Tom
Super User Tom
Super User

@dxiao2017 wrote:
This is too much information, could you summarize the usage of %local statement for example using no more than 4 simple bullet points, thanks a lot
  • Always play nice with others.
  • Define the macro variables you create in your macro using %LOCAL statement (or using 'L' as the third parameter in any CALL SYMPUTX() calls) so you do not modify already existing macro variables of the same name.
    • Unless that is the purpose of the macro.
dxiao2017
Lapis Lazuli | Level 10
Thanks a lot for letting me know this, Tom! I did not know that if I do not write %local in my macro, my macro will overwrite others' macro of the same name. I do not have a job now and I am learning SAS by myself and I have just started learn macro, so I had no chance to make such big mistake (e.g., overwrite and mess up existing macros and get fired, that sort of mistake is what I tend to make I guess) by far. If I am lucky enough and get a job, this suggestion would be one of the best I get.

hackathon24-white-horiz.png

2025 SAS Hackathon: There is still time!

Good news: We've extended SAS Hackathon registration until Sept. 12, so you still have time to be part of our biggest event yet – our five-year anniversary!

Register Now

Autotuning Deep Learning Models Using SAS

Follow along as SAS’ Robert Blanchard explains three aspects of autotuning in a deep learning context: globalized search, localized search and an in parallel method using SAS.

Find more tutorials on the SAS Users YouTube channel.

Discussion stats
  • 35 replies
  • 8371 views
  • 22 likes
  • 5 in conversation