I am trying to set default variables for my macro parameters. I found this example on a site:
%macro myprint (var1, var2=t);
PROC PRINT data = &var1..&var2;
Run;
%mend myprint;
/* Macro Call */
%myprint (store,)
But when i try to run it I get ERROR: More positional parameters found than defined.
I am using SAS 9.2. Is there something different I have to do?
There are two kinds of parameters in SAS Macros: Positional parameters and named parameters. It is possible to mix them, although generally speaking a bad idea.
Named parameters are like your var2=t. They must be specified explicitly by name in the call. This is actually a 'best practice' much of the time; it ensures you have the right parameters defined (how easy is it to forget the order of the parameters in a macro?).
Named parameters can be used in any order, and can be left out (whether or not they have a default value). For example,
%macro myprint(var1=,var2=t);
...
%mend;
That would set a default for var2, but not for var1; it still must either be provided, or it will evaluate to a blank (which may be okay or may not be).
Positional parameters are exactly what they sound like: parameters defined by the location in the macro definition. They must be provided in order (of course) and if they are left out they default to blank. They cannot be provided a default value. Positional parameters must precede named parameters - ie, you could not have done
%macro myprint(var2=t,var1);
as that would confuse things too much.
So in your case, if you want to keep the 1 named 1 positional, just remove that extra ',' when you don't provide var2.
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 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.
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;
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
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.