SAS define %LET macro variable with a double ampersand macro variable - sas

I am trying to create a local macro variable with the %LET statement, the value of which is a macro variable that requires multiple ampersands to resolve. SAS is not resolving the multiple ampersands before assigning the name of the local macro variable.
%macro example;
%do i=1 %to %sysevalf(&max_n);
%let dg= &&max_&i..;
{stuff happens here}
%end;
%mend;
%example;
For example, &max_1. resolves to APPLE which I use in the {stuff happens here} portion of the code. However, SAS is giving me the warning
"WARNING: Apparent symbolic reference MAX_ not resolved."
And the DG macro variable is returning &&max_1 through &&max_17. Calling &DG. at this point will return &&max_1 which will resolve to APPLE on its own, but this will not work with the syntax in the code I'm not showing.
Any idea how to make the macro variable so that &DG. will return APPLE?

(I know this is not an answer but I cannot post formatted code in comments. I will edit/remove this post afterwards).
The code you posted here is not your culprit. If I take it as-is, initialize a couple variables in front and put a %put statement where stuff happens:
%let max_1=APPLE;
%let max_n=1;
%macro example;
%do i=1 %to %sysevalf(&max_n);
%let dg= &&max_&i..;
%put &dg;
%end;
%mend;
%example;
You can copy/paste this and run it and it will print 'APPLE' to your log as expected.
There must be something more to what you are trying to do that is causing your issue.

Also, the macro variables are not resolved (or not needed to exist in parent or current scope) until macro execution time when the macro is invoked. Thus, the assignment of max_1 and max_n can occur after the macro definition and before the %example.
The behavior you are experiencing could be due to macro quoting you applied to the variables prior to the macro invocation. You can either use %unquote within the macro at callee scope, upon the macro resolution at caller scope, or determine how to process your concepts and inputs with reduced amounts of quoting.
Note: Numerically indexed macro variables are sometimes called 'macro arrays'. macro variables suffixed with symbols are sometimes called 'macro associative arrays' or 'macro objects'

Related

SAS CALL SYMPUT not working inside a macro (and before it did)

I need to store in a macro variable the number of rows in a data set. So I used this:
%macro get_table_size(inset,macvar);
data _null_;
set &inset NOBS=size;
call symput("&macvar",size);
stop;
run;
%mend;
%get_table_size(LOANTAPE.INSTRUMENT_VU21,NUM_REG_INS);
%put &NUM_REG_INS;
Before my computer crashed (having to force it to reboot with SAS opened), this worked (I swear xd). But now, the macro NUM_REG_INS is not created. The log says: Apparent symbolic reference MACVAR not resolved.
So I checked the code as a data step and not a macro, like this:
data _null_;
set LOANTAPE.INSTRUMENT_VU21 NOBS=size;
call symput("macvar",size);
stop;
run;
%put &macvar
And it works. So the problem is when using this code inside a macro. Does anybody knows what could be happening here and how to fix it? And, just for the sake of curiosity, why was it working before?
Thank you, really!!
Variable scope. Look into using CALL SYMPUTX() instead of CALL SYMPUT(). It likely worked before because you either created the macro variable globally while testing and in this case you haven't. Macro variables do not exist outside the macro unless you create them as a global macro variable.
call symputx("&macvar", size, 'g');
See the documentation here
If you want to access the macro variable outside of the macro then make sure it is not defined as only local to the macro. You could use the third parameter of call symputX to force the update of a global macro variable. But then you could have trouble trying to call this macro to update a macro variable that is local to the calling macro.
Just add a line to the macro to force the macro variable to be global when it does not exist. You should also use the newer CALL SYMPUTX() function, unless you really wanted those leading spaces in the value of macro variable that the automatic conversion of SIZE to a character string would have generated by using the older CALL SYMPUT() function.
Also move the CALL SYMPUTX() to before the SET statement so it runs even if the dataset has zero observations.
%macro get_table_size(inset,macvar);
%if not %symexist(&macvar) %then %global &macvar;
data _null_;
call symputx("&macvar",size);
set &inset NOBS=size;
stop;
run;
%mend;

The advantages of defining macro variables outside the macro function

I have a stubborn lecturer who insists that defining all macro variables inside the parenthesis of the macro statement like this
%MACRO TEST(Var1= , Var2= , Var3= );
What are the advantages of this? What are the advantages of actually defining your function like this instead:
%LET var1= <Insert long list of 50 variables here>;
%LET var2= <name of input data>;
%LET var3= <group by variables>;
%MACRO TEST;
I argue that the second option provides clarity and a neat coding structure, could anyone point out any other advantages or disadvantages of the two methods?
Two main points:
Use of global variables is widely considered to be bad practice.
Using your system, how would you write out multiple calls to the same macro in different places in your code? How would you keep track of which parameter lists correspond to which macro calls?
Macro variable scope - having variables only available within the macro ensures that any previously declared macro variables are not used by accident. If you accidentally run things out of order, with your method you're likely to run into issues.
It makes it clear what parameters the macro requires - otherwise you have to read the code, find all the & and declare them at the top.
Less typing overall
You can set default values within the parameter list and then only list/call what options you need when declaring.
Macro definition:
%macro test(var1 = , var2 = , var3 = 25);
Macro call/execution:
%test(var1 = 5, var2 = 4);
What value will var3 have in the macro?
You're still using very simple cases and a lot of the more complicated usages work better when you have parameters. Consider the case with calling the same macro 50 times for different parameters which happen to be in a data set. You can use CALL SYMPUTX() for each but then you'll run into timing issues of when the macro is called and such. Whereas using CALL EXECUTE and the parameters inline makes it very easy.
PS. In general, the odds are 99% your lecturer will be correct at this point in time when you're starting out. Assuming that will help you frame your questions differently, rather than trying to prove someone wrong (which is how your question is coming across) you'll be looking at understanding how something works. Also, it's possible that your lecturer will be online as well so if they see your questions at some point you won't come across as a know-it-all kid. Ultimately that's your choice though.
It depends on how the macros will be used. Global macros can be very helpful and as you pointed out provide clarity if they are used correctly. For example, if I have a bunch of SAS programs that need to run in order to generate data sets or reports, I would put them in a wrapper program and use global macros.
%Let year = 2019;
%Let State = CA;
%let Dept = DOE;
%macro MakeRpt;
%include "MakeData.sas";
.
. more %include statements
.
%include "GenerateReport.sas";
%mend;
%makeRpt;
However, if I'm making a macro "utility" that will be called by a user whenever they need it, using local macros makes the most sense. It's really a question of how a macro will be used as to whether global or local makes more sense.
The only time you'd ever want to do this is if you have global variables that will appear throughout the program. For example, it is not uncommon to have special setup or initialization programs to hold commonly-referred values, especially when going between development and production. This can make things easier to handle when promoting a program, or easier to adjust if certain things change later on (such as a directory location or hostname).
For example, the below macro can change some global macro variables to point to certain directories that differ between two servers depending on where the code is run.
%macro dev_prod;
%global directory inlib outlib;
%if(&syshostname. = production-server.company.com) %then %do;
%let directory = C:\prodlocation;
%let inlib = C:\prodlib;
%let outlib = C:\outlib;
%end;
%else %if(&syshostname. = dev-server.company.com) %then %do;
%let directory = C:\devlocation;
%let inlib = C:\devlib;
%let outlib = C:\outlib;
%end;
%mend;
%dev_prod;
In general, you want to use local macro variables in macros that perform specific functions. For example, the below macro regresses on variables on a dataset:
%macro regression(data=, dep=, indep=);
proc reg data=&data.;
model &dep. = &indep.;
run;
%mend;
%regression(data=sashelp.cars, dep=horsepower, indep=msrp);

SAS Macro in macro

I have one question regaring %macro.
Can I set %macro in another %macro?
Short example - "picture" of situation:
%macro Tier_1();
%do Iter = 1 to &i;
%macro Tier_2()
proc sql noprint;
select
1*&Iter into :var
from work._PRODSAVAIL
;quit;
%put &var;
%mend;
%Tier_2();
%end;
%mend;
%Tier_1();
The answer to your question is "yes, it is possible." But it's poor style. The identical results from above will occur if you simply move the macro definition for %Tier_2 outside of macro Tier_1, but leave the call inside it.
%macro tier_1();
...
%Tier_2();
%mend tier_1();
%macro tier_2();
...
%mend tier_2;
%tier_1();
As you see above, you don't even have to order them in a particular way - as long as both are compiled before the execution of the macro it will work fine.
The only time it would make sense to put a macro definition inside another macro definition would be if the outer macro modified the inner macro in some way, and so it was necessary to re-compile the inner macro each time the outer macro executes.
While that's a theoretical use case, I don't think it's one you are likely to encounter in practice; there are lots of other ways to modify things without actually modifying the macro code itself, and as such it's considered poor programming style and should be avoided. You're adding (minimal, but some) overhead for no real benefit, and making it harder to understand the code.
The definitions are NOT logically nested. There is just a flat space of macro names. If you define the same %submacro inside of %macroA and %macroB there will only be one %submacro, which ever definition ran most recently.
You can nest macro CALLS (call a macro as part of a macro) but nesting the source code of the macro definitions is not a good idea. You can do it, but it will just confuse you.

Does a macro variable created in PROC SQL become local or global

When I write:
proc sql;
select count(*) into :out from sashelp.cars;
quit;
Does the macro variable out become a global or local variable?
It depends. Let's put together a test macro and see what happens
%macro test();
proc sql noprint;
select count(*) into :x from sashelp.cars;
quit;
%put IN MACRO: &x;
%mend;
options nosource nonotes;
%symdel x;
%test();
%put Out MACRO: &x;
%let x=2;
%put Out MACRO: &x;
%test();
%put Out MACRO: &x;
Creates:
IN MACRO: 428
WARNING: Apparent symbolic reference X not resolved.
Out MACRO: &x
Out MACRO: 2
IN MACRO: 428
Out MACRO: 428
So at the beginning, there was no X macro variable. The %test() macro populated a local variable. It was not available outside the macro.
After that, we set %let x=2 making X be a global variable. Then we execute the %test() macro and see that X keeps its value outside of the %test() macro.
So if it exists globally, it continues to exist and is overwritten. If it doesn't exist globally, it continues to not exist globally.
Let's take a look at the documentation to answer this compeltely.
The documentation for SQL INTO clause in the Macro language dictionary says:
Macro variables created with INTO follow the scoping rules for the %LET statement. For more information, see Scopes of Macro Variables.
So, we click on that link, and find this page - How Macro Variables are Assigned and Resolved and this page - Examples of Macro Variable Scopes.
The first page has a nice tree diagram showing the decisions SAS makes when assigning or creating a macro variable, and that might be sufficient to understand this.
The second page has some good examples and explanations:
When the macro processor executes a macro program statement that can create a macro variable (such as a %LET statement), the macro processor attempts to change the value of an existing macro variable rather than create a new macro variable. The %GLOBAL and %LOCAL statements are exceptions.
There are some very good examples on that page; what it boils down to, though, is simple. If there is already an existing macro variable with that name, it will assign the value to that macro variable, wherever that variable is - in the scope tree of the current macro, anyway. (So, a single macro executing has two symbol tables to look in: local, and global. A macro called by another macro has three: local, local to the calling macro, and global. Etc.)
However, there are a pair of related exceptions: when you specify the table to use, with %local or %global. Either of those statements referencing a macro variable before your %let or select into will cause any following statements to set that particular version - local or global - and then you're safe for sure.
It depends on where you define this marco variable, if you define it in a macro program, it is a local macro variable, otherwise it is a global macro variable.

Why does the put and let statements need to be prefixed by a % sign in SAS

In SAS, why cannot we write
let name = abc;
put "&name";
Why do we have to include the % sign like this:
%let name = abc;
%put &name;
Imagine I am writing the statements in the main body of the code, not inside a data step.
Also, is the second way of writing it same as:
%macro test;
%let name = abc;
%put &name;
%mend;
The %LET and %PUT statements are part of the SAS macro processor and not part of base SAS. The % (and &) triggers are what activate the macro processor and allow it to recognize that these strings need be processed before they are passed to the SAS compiler/interpreter.
You cannot use an assignment statement like
x = 3.5 ;
outside of a data step (or some proc that support these types of statements).
To your second question, if you wrap the macro statements inside of a macro definition then the main impact will be.
The macro variable NAME will be defined as local to the macro if it does not already exist.
Nothing will happen until you invoke the macro. The %macro statement begins the definition of a macro. So all of the code up to the corresponding %mend statement define the macro. To execute it you will need to invoke the macro using syntax like %test.
%let and %put are part of the SAS macro language. Macro language statements are (with one or two particular exceptions) prefixed by % to tell the SAS macro parser to operate on them.
They do entirely different things from the non-% version - except when it works out to the same thing. You can write put "&mvar."; - as long as it's in a data step (As that's a data step statement). Macro commands/functions/statements are allowed in open code sometimes (and not in others).
Writing it inside an actual macro is more-or-less the same. There are scoping issues, though; &name won't be available outside of that macro, unless it's been declared global.