%let a = first;
%let b = last;
%let &a._and_&b = next;
&put first_and_last = &first_and_last;
I have used it to dynamically create a variable -- note _&dummy in the following code:
%put RP Note: inside macro getpwd(user,dummy) ;
%put - parameter user = &user ;
%put - parameter dummy = &dummy ;
%local dsid rc user_id varN _&dummy;
%if (&dsid > 0) %then %do;
%let varN = %sysfunc(varnum(&dsid,password));
%if (&rc = 0) %then %do;
%let _&dummy = %sysfunc(getvarc(&dsid,&varN));
%if %length(&dummy) > 0 %then %let &dummy = &&&&_&dummy ;
%else &&&&_&dummy ;
%else %put RP ERROR: failed to retrieve an observation from misc.users for user_id=&user ;
%else %put RP ERROR: failed to open misc.users ;
%put RP Note: finished getpwd ;
dummy is an external macro variable that was supplied to hold the returned value of the macro (a user's password). This was done to support a bunch of legacy code that made the call:
The original getPWD retrieved the password from a file in such a way that the following code would not work:
libname my_oracle oracle user="%getID" password="%getPWD(%getID)" ... ;
but the new code does.
So, both the old method (legacy code) is not broken, and the new method is simpler to use.
I enjoyed your post and your well-crafted macro code. I, too, strive to minimize maintenance disruption by honoring legacy interfaces while providing additional function without muddling the code.
Having an interest in macros myself, I shanghaied your code into my SAS laboratory to see how it works, especially that impressive quadruple ampersand piece! In my experience, I have never needed more than triple ampersands to produce the required indirection. More than triple ampersands seem to me to obscure the logic. So I wanted to see why you included a quad!
As I studied your code in the following test bed, it appeared to me that I might be able to simplify the 2 quads to doubles and yield the same result. When I attempted it, it seemed to work as the original. What are your thoughts?
OPTIONS MPRINT MPRINTNEST MLOGIC;
INPUT user_id : $8.
password : $25.
I found the idea of using 4x& from a posting on a different website for something that I was looking for and can't remember what it was, nor where the site was, etc.
What I do remember is that I tried &, &&, &&&, and &&&&, and found that I really did need all four &'s for the macro to work properly in all cases.
case 1 = %local password; %getPWD(&userID,password);
case 2 = %global password; %getPWD(&userID,password);
case 3 = %getPWD(%getID)
OleDB.sas in MacLib -- which is part of "options sasautos=(MacLib SASAUTOS)"
datasource='dns_name' provider=sqloledb user="&user" password="%getPWD(&user)" schema=dbo prompt=no
OleDBlib.sas in IncludeLib
Libname my_db OleDB %OleDB;
then inside other SAS code
connect to oledb (%OleDB);
disconnect from oledb;
and other similar kinds of things. Basically all the previously existing code, or at least representative samples.
While the other examples are truly magnificent examples, here's something a bit simpler (I hope):
** define a generic macro program for sashelp.class;
** get rid of periods in WANTVAL for filename;
%let x = %sysfunc(translate(&wantval,'_','.'));
%let outfile = Apr08_&wantvar.&x;
%let wantvar = %upcase(&wantvar);
title "Report for &wantvar = &wantval";
title2 "Result File is: &outfile..html";
%if &wantvar = AGE or &wantvar = HEIGHT or
&wantvar = WEIGHT %then %do;
%let whrcls = where &wantvar EQ &wantval;
%else %if &wantvar = NAME or &wantvar = SEX %then %do;
%let whrcls = where &wantvar EQ "&wantval";
%let whrcls = %str(where 1);
title 'You did not specify name, age, height, weight or sex';
title2 'for &wantvar. check your program and submit again';
title3 'Printing all OBS for SASHELP.CLASS';
Basically, this is a definition for a macro program. I am using it to build code for me so that, when the macro program is invoked, the generated code (when executed) will select observations out of SASHELP.CLASS. The macro variables that I will provide are &WANTVAR (for which variable they want -- name, age, height, weight, sex) and &WANTVAL will be the VALUE that somebody is asking for. My condition is always an EQ condition in the WHERE clause. But for every time I run the Macro program (%getobs), I want a separate file name to be generated that looks like this: Apr08_xxxxxxxyyyyy.html where xxxxx is the value for &WANTVAR and yyyyy is the value for &WANTVAL. AND, in order to build the correct WHERE clause, I have to know whether they asked for AGE, HEIGHT and WEIGHT for &WANTVAR or whether they asked for NAME or SEX -- because the WHERE clause would look different for character variables versus numeric variables. AND, since height and weight could have decimal points in the number, I need the decimal in the &WANTVAL macro variable, but I do NOT want the . in my file name.
So, the %let for X is getting rid of the . out of &WANTVAL and the %let for OUTFILE is building the special file name that I want. And, in addition to that, the %let for WHRCLS is being set conditionally depending on what they asked for. So, from 2 pieces of information provided to my macro program, I have done these OTHER things, just by creating new macro variables:
1) generated a file name for my HTML file based on the values they sent me (including stripping out the . and replacing it with _)
2) generated the correct where clause for character vs numeric variables
Then when I invoke that macro program, I will get the desired results for the first 4 invocations and all the obs in SASHELP.CLASS as a result of the last invocation. I'm not going to show all the output below, but I will show all of the TITLES from each run:
** now use the macro program multiple times;
** note in the log how the where statement and output file;
** name get changed appropriately depending on wantvar and wantval;
%let wantvar = age;
%let wantval = 13;
Report for AGE = 13
Result File is: Apr08_age13.html
You did not specify name, age, height, weight or sex
for &wantvar. check your program and submit again
Printing all OBS for SASHELP.CLASS
This is just ONE example of how you can build new macro variables from existing macro variables. There are many, many more. Generally, you use this feature of the macro facility because you need something in your generated code that can be derived from an existing macro variable.