I have macros in my code that are created and used in random order.
It's showing "reference not resolved" for "%put &pincuk", and syntax error for the "&pincuk". It works fine when I run the code twice. I'm guessing it happens when SAS is reaching the &pincuk before the macro is created. For example,
data x.fcastukcalc;
do day=&daycountuk + 1 to &daycountkorea
retain fcast &ukdmax;
fcast=(fcast * &pincuk) + fcast;
output;
end;
run;
/* then this, later on */
data _null_;
keep deathsuk inc1 inc2 pinc1 pinc2 pinc;
set x.uk;
inc1=deathsuk - lag1(deathsuk);
pinc1=(inc1 / lag1(deathsuk));
inc2=lag1 (deathsuk) - lag2(deathsuk);
pinc2=(inc2 / lag2(deathsuk));
pinc=(pinc1 + pinc2) / 2;
call symputx('pincuk', pinc);
run;
%put &pincUK;
If, on the first run through, x.uk does not exist, or has zero rows, the call symputx('pincUK',pinc); will never be reached. So, in a 'random' situation of code running, lets be more generous and say a developmental situation, your expectations may have been subverted by a misremembered operation or subtle change of state. Check your code for %SYMDEL statements. During development of a large macro, you may be submitting parts of the interior of a macro and not have complete 'simulation' of the state expected to exist during an actual call of the macro.
Start a fresh SAS session and see if the problem with the code persists and can be reproduced more directly.
To be specific, your question is about macro symbols, often referred to as macro variables. Macros themselves are names for groups of SAS programming statements.
From help (good read)
When SAS compiles program text, two delimiters trigger macro processor activity:
&name
refers to a macro variable. Replacing Text Strings Using Macro Variables explains how to create a macro variable. The form &name is called a macro variable reference.
%name
refers to a macro. Generating SAS Code Using Macros explains how to create a macro. The form %name is called a macro call.
The text substitution produced by the macro processor is completed before the program text is compiled and executed. The macro facility uses statements and functions that resemble the statements and functions that you use in the DATA step. An important difference, however, is that macro language elements can enable only text substitution and are not present during program or command execution.
------ Edit (added) ------
Writing code with lots of abstractions (i.e. macro variables) requires a certain level of discipline and system design. A macro must be compiled before it can be called, however the macro variables (i.e. symbols) it resolves within do NOT need to exist at compilation time, only at macro invocation (call) time. For old fuddies the concept is like a mail merge boilerplate with too many fields.
Macro variables can be local (as a parameter in the macro definition, or explicitly stated in a %LOCAL, or as an assignment to a previously undefined symbol. Reliance on GLOBAL macro variables should be reduced to a minimum or none, as should an over-reliance on variables to be expected to exist in the caller scope. Dependence on a variable being global should be explicitly stated with a %GLOBAL in the macros source.
An assignment to a non-declared %LOCAL can be a problem because the assignment could accidentally (unexpectedly) replace the value of a declared or existing variable in an outer (or calling) scope, and be the cause of it don't work right problems. Good discipline is to explicitly %LOCAL all variables within a macro definition - the macro system does not have a strict mode (as found in other languages) that reports problematic macro variables.
Related
The following variables seem to be a standard macro in multiple SAS Code that I come across. Can someone explain the following please?
&dsin.
&dsout.
&cj_yyyymm_1.
&cj_yyyymm_2.
No, those are not "standard" macro variables. There are automatic macro variables, which you can view with
%put _automatic_;
And some other system-generated macro variables are sometimes just stored as regular global macro variables, which you can view with:
%put _global_;
or
%put _all_;
Which will print all currently defined macro variables - run it at startup and you'll see just the ones SAS defines.
What you show there are macro variables that perhaps are standard for your company, but don't have any standard meaning. I would posit that &dsin is an input dataset to a macro, and &dsout is an output dataset, and the other two are year/month stamped variables, but they don't have any official, standard definition, nor would I say those are particularly commonly seen.
Those are not generic, they are specific to your program or company but you can make educated guesses. DS is a common abbreviation for data set.
&dsin. = Input data set
&dsout. = Output data set
&cj_yyyymm_1. = some date parameter, probably like 202110
&cj_yyyymm_2. = some other date parameter....
CJ could mean something specific at your company or may reference something in your code.
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.
In a modern language (e.g.python), you could do something like
def do_a_thing(foo,bar):
thing = (... do a thing to foo(bar) ...)
return thing
How does one do this (or something similar enough) in SAS? In my concrete application I have defined a bunch of functions, and need to do the same thing to all of them, so I thought it would be nice to have a function that takes a function as an argument and then does the thing to that function, and then apply it where needed. The "obvious" solution doesn't work, e.g. in a proc fcmp doing this:
function do_a_thing(foo,bar);
thing = (... do a thing to foo(bar) ...)
return(thing);
endsub;
This fails because SAS doesn't know about any function called foo, and throws an error.
I expect the answer involves some macro trickery, but I find the macro system somewhat opaque and can't quite figure it out. What's the best way to do this?
Show your code regarding defined a bunch of functions.
Macro, at it's core, is a text generating system with side effects. Macro can perform dispatch like processing using indirect resolution -- see answer Invoke a Macro Using A Macro Variable Name
If you are trying to code a general purpose function invokable from DATA Step, SQL, %SYSFUNC, or DS2 both Proc FCMP and DS2 can create user defined functions (UDF). The method(or function)-name to invoke (or dispatch, or APPLY) would likely have to be passed as a string into said UDF.
You will also want to look into DOSUB and DOSUBL
Details
The DOSUBL function enables the immediate execution of SAS code after a text string is passed. Macro variables that are created or updated during the execution of the submitted code are exported back to the calling environment.
DOSUBL returns a value of zero if SAS code was able to execute, and returns a nonzero value if SAS code was not able to execute.
As for modern... SAS SCL had CALL APPLY ages ago -- sadly SCL never made it to the Foundation product or escaped the confines of SAS.
You haven't really shown an example where this might be required (or even useful).
But in general in SAS you would use code generation to implement that type of mis-direction. For example your second "function" could be a statement style macro. That is macro that only emits part of a statement to be included into the actual SAS program you want to create.
%macro do_a_thing(function,arglist);
&function(&arglist)
%mend;
Then you might use it in a program
data want ;
set have ;
mean = %do_a_thing(mean,of _numeric_);
std = %do_a_thing(std,of _numeric_);
run;
For more complex things you will have more trouble. The new-ish DOSUBL() function might help in that they can allow you to run multiple steps in a separate execution space. But for most things the performance cost might be too high to make it worth while.
Don't really know anything about SAS, but in general you would need some way to make a difference between make a function call and passing a parameter..
A Google search for the title of this question led me here, perhaps it might help you:
https://communities.sas.com/t5/SAS-Procedures/how-to-pass-a-parameter-with-macro-variables-into-macro/td-p/330627#messageview_4
Let us say I write a SAS macro and in that macro, I wish to invoke the macro function TRIM.
Do I need to write:
%sysfunc(%trim(&text.))
Or %trim(&text.)
Or
%sysfunc(trim(&text.))
What is the correct syntax?
The basic rule regarding SYSFUNC or not SYSFUNC:
If you are running a function from this page, the Dictionary of SAS Functions and Call Routines, then you use SYSFUNC.
If it's on this page or the related pages ("Autocall Macros" is relevant here), then you use % and don't use SYSFUNC.
In some cases, and %trim is one of them, you can do both - getting more or less the same result (there may be small differences in how the SYSFUNC and the autocall macro work, particularly related to macro quoting).
There is no macro function trim. So it depends on whether you want to call the SAS supplied autocall macro %TRIM() or if you want to use the function TRIM(). If the later then you need to nest it inside the macro function %SYSFUNC() using %SYSFUNC(TRIM()).
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.