BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
SasStatistics
Pyrite | Level 9

I would like to create a Macro that can take a variable number of arguments. 

Please note that the psuedo-program that I write below perhaps could bypass the need for variable number of arguments, but the point with the programe is to demonstrate my need (In reality it is a more complex program that I am writing). 

So the following program shows that I would like to create a function that can take arbitrary number of arguments and then sum them.

%Macro MyMacro(X1, X2, + ...); 
   sum = X1 + X2 + ... ;
%mend; 

If I knew that I would like to sum only two numbers, it would be very easy: 

%Macro MyMacro(X1, X2); 
   sum = X1 + X2;
%mend; 


But I don't know how many numbers I want to sum (it depends), so the question is, how could I allow for an arbitrary number of arguments?

 

1 ACCEPTED SOLUTION

Accepted Solutions
PaigeMiller
Diamond | Level 26

@SasStatistics wrote:

I would like to create a Macro that can take a variable number of arguments. 

Please note that the psuedo-program that I write below perhaps could bypass the need for variable number of arguments, but the point with the programe is to demonstrate my need (In reality it is a more complex program that I am writing). 

So the following program shows that I would like to create a function that can take arbitrary number of arguments and then sum them.

%Macro MyMacro(X1, X2, + ...); 
   sum = X1 + X2 + ... ;
%mend; 

If I knew that I would like to sum only two numbers, it would be very easy: 

%Macro MyMacro(X1, X2); 
   sum = X1 + X2;
%mend; 


But I don't know how many numbers I want to sum (it depends), so the question is, how could I allow for an arbitrary number of arguments?

 


This isn't a situation where you need a variable number of arguments. It is a situation where you need one argument that can have a different number of values each time you run the macro.

 

%Macro MyMacro(X); 
   %let x=%sysfunc(translate(&x,%str(+),%str( )));
   sum = &x;
%mend; 

options mprint;
data want;
     %mymacro(2 4 6)
run;
data want2;
     %mymacro(2 4 6 8 10 12 14)
run;

Or even more general solution

 

 

%Macro MyMacro(X); 
   %let x=%sysfunc(translate(&x,%str(+),%str( )));
   &x
%mend; 

options mprint;
data want;
     sum1=%mymacro(2 4 6);
     sum2=%mymacro(2 4 6 8 10 12 14);
run;

 

 

 

 

--
Paige Miller

View solution in original post

16 REPLIES 16
PaigeMiller
Diamond | Level 26

@SasStatistics wrote:

I would like to create a Macro that can take a variable number of arguments. 

Please note that the psuedo-program that I write below perhaps could bypass the need for variable number of arguments, but the point with the programe is to demonstrate my need (In reality it is a more complex program that I am writing). 

So the following program shows that I would like to create a function that can take arbitrary number of arguments and then sum them.

%Macro MyMacro(X1, X2, + ...); 
   sum = X1 + X2 + ... ;
%mend; 

If I knew that I would like to sum only two numbers, it would be very easy: 

%Macro MyMacro(X1, X2); 
   sum = X1 + X2;
%mend; 


But I don't know how many numbers I want to sum (it depends), so the question is, how could I allow for an arbitrary number of arguments?

 


This isn't a situation where you need a variable number of arguments. It is a situation where you need one argument that can have a different number of values each time you run the macro.

 

%Macro MyMacro(X); 
   %let x=%sysfunc(translate(&x,%str(+),%str( )));
   sum = &x;
%mend; 

options mprint;
data want;
     %mymacro(2 4 6)
run;
data want2;
     %mymacro(2 4 6 8 10 12 14)
run;

Or even more general solution

 

 

%Macro MyMacro(X); 
   %let x=%sysfunc(translate(&x,%str(+),%str( )));
   &x
%mend; 

options mprint;
data want;
     sum1=%mymacro(2 4 6);
     sum2=%mymacro(2 4 6 8 10 12 14);
run;

 

 

 

 

--
Paige Miller
SasStatistics
Pyrite | Level 9

Thanks @PaigeMiller, what my actual function need is to be able to put in one argument with several table names (in retrospect, my pseudo program did not catch what I wanted i realize...). 

So for instance argument x could look like: 
x = [Table1, Table2, Table3, ...]. 

Something clever exists for this? 

PaigeMiller
Diamond | Level 26

@SasStatistics wrote:

Thanks @PaigeMiller, what my actual function need is to be able to put in one argument with several table names (in retrospect, my pseudo program did not catch what I wanted i realize...). 

So for instance argument x could look like: 
x = [Table1, Table2, Table3, ...]. 

Something clever exists for this? 


What are you going to do with the several table names??


Again, I don't think you need several macro arguments, I think you need one argument that can contain several table names ... but until I know what you intend to do with the table names, I can't be sure.

--
Paige Miller
SasStatistics
Pyrite | Level 9

" I think you need one argument that can contain several table names", exactly what I mean (and think I wrote in the reply?).

The function I would like to create will from each table name extract all the columns in the table and print it out so the user can get an overview.

Thanks @PaigeMiller .

PaigeMiller
Diamond | Level 26

@SasStatistics wrote:


The function I would like to create will from each table name extract all the columns in the table.


Extract all the columns? Or extract all the column names?

 

Assuming you mean column names, here is some code that also uses the %qlist macro (at https://github.com/sasutils/macros/blob/master/qlist.sas). 

 

%macro colnames(arg);
    %let q_arg=%qlist(&arg);
    proc sql;
        create table colnames as select libname,memname,name
        from dictionary.columns where upcase(memname) in %upcase(&q_arg);
    quit;
%mend;

%colnames(cars want)

I'm sure there are ways to do this without %qlist, but I'm so used to using %qlist that I don't ever bother to try to figure out other ways to do it without %qlist.

 

--
Paige Miller
SasStatistics
Pyrite | Level 9

I wanted to extract all column names. 

Running the code supplied: 

%macro colnames(arg);
    %let q_arg=%qlist(&arg);
    proc sql;
        create table colnames as select libname,memname,name
        from dictionary.columns where upcase(memname) in %upcase(&q_arg);
    quit;
%mend;

%colnames(cars want)

yields the following error: 

WARNING: Apparent invocation of macro QLIST not resolved.


WARNING: Apparent invocation of macro QLIST not resolved.
NOTE: Line generated by the macro function "UPCASE".
36          %QLIST(CARS WANT)
            _
            22
            76
WARNING: Apparent invocation of macro QLIST not resolved.
ERROR 22-322: Syntax error, expecting one of the following: (, SELECT.  

ERROR 76-322: Syntax error, statement will be ignored.

 

Tom
Super User Tom
Super User

You need to define macros before using them (or at least same them in your autocall library so SAS can find them to autocompile).

 

Here is a QLIST macro that would probably work with the program.

https://github.com/sasutils/macros/blob/master/qlist.sas

 

PaigeMiller
Diamond | Level 26

You have to place the code for %qlist in your code before %colnames, and then execute both. I put %qlist (and many other useful macros) in my AUTOCALL library, so I don't need to copy and paste the code for the %qlist macro every time I want to use it. The AUTOCALL library is in my AUTOEXEC so every time I launch SAS, these macros are made available to me. If you are going to be writing a lot of macros, you should set up an AUTOCALL library. Here is the command in my AUTOEXEC file and all of my utility macros are stored in that folder..

 

options sasautos=(sasautos 'myserver\MillerP\Macros');

  

--
Paige Miller
SasStatistics
Pyrite | Level 9
If the function I am using will be used by other people, what would be best then? To include the code for the %qlist macro in my macro?

PaigeMiller
Diamond | Level 26

@SasStatistics wrote:
If the function I am using will be used by other people, what would be best then? To include the code for the %qlist macro in my macro?


The answer is a great big "it depends" and also, as I'm sure you know, there are "many ways to skin a cat" as the expression goes (although I can't verify that the expression is true as I have never tried doing that).

 

Certainly, you can place %qlist in the same file as %mymacro and named mymacro.sas (again, better names are needed) and then distribute this SAS file to whomever you'd like (or use the code from @Tom which doesn't require %qlist).

 

If everyone can access your server where mymacro.sas is stored and where qlist.sas is stored as separate files, then they can add this as the first line of their code and then suddenly, like magic, they can access both qlist.sas and mymacro.sas and any other macro in the indicated folder, and now %qlist does not have to be defined inside mymacro.sas

 

options append=(sasautos="myserver\MillerP\Macros");

The benefit of doing it this way on a server that everyone can access is that if you fix a bug or add a new feature to your macro, everyone using it this way gets the  changes instantaneously, with zero effort on their part. If you have to include %qlist in the file and then perhaps e-mail it to others, then when you update your copy, the recipients of the e-mail still have the old version.

 

There is also a way to call the qlist.sas file directly from github in your program and so you just need one command in your code to access %qlist. Not everyone can do this, there are some restrictions on what external files I can access from my company computer becuase of the company firewall; and so I don't know the command, but I'm sure others do. Again, it depends on if this will work behind your company's firewall.

--
Paige Miller
SasStatistics
Pyrite | Level 9

Haha I am sure there is although I have not tried it either... 😉 

"If everyone can access your server where mymacro.sas is stored and where qlist.sas is stored as separate files, then they can add this as the first line of their code and then suddenly, like magic, they can access both qlist.sas and mymacro.sas and any other macro in the indicated folder, and now %qlist does not have to be defined inside mymacro.sas"

This is the alternative I will go for. What do you mean by: "then they can add this as the first line of their"?

PaigeMiller
Diamond | Level 26

The first line of the program they write to use your macros should be the OPTIONS statement I provided.

--
Paige Miller
Tom
Super User Tom
Super User

@SasStatistics wrote:

" I think you need one argument that can contain several table names", exactly what I mean (and think I wrote in the reply?).

The function I would like to create will from each table name extract all the columns in the table and print it out so the user can get an overview.

Thanks @PaigeMiller .


Supply the list of names as the value of one parameter.

%macro mymacro(members);
proc print data=sashelp.vcolumn;
  where findw("&members",memname,' ','it');
  var libname memname varnum name type length format label;
run;
%mend mymacro;
%mymacro(class cars)

Ready to join fellow brilliant minds for the SAS Hackathon?

Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.

Register today!
How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 16 replies
  • 1486 views
  • 6 likes
  • 4 in conversation