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

Hi

I am trying to generate X_1 = A_1; X_2 = A_1 A_2; ...X_5 = A_1 A_2 A_3 A_4 A_5; and following code does the job

%macro temp;
%let x_1 = A_1;
%do i= 2 %to 5;
%let x_&i. = &&x_%eval(&i.-1). %str( A_&i.);
%put &&x_&i.;
%end;
%mend;
%temp;

The issue is on every iteration it's trying to resolve &X_ as well and thus throwing a warning!

What am I doing wrong and how can I rectify it?

Thanks a lot in advance.

1 ACCEPTED SOLUTION

Accepted Solutions
Quentin
Super User

Good question!

 

The problem is that you're trying to use && to create an indirect macro reference:

%let x_&i. = &&x_%eval(&i.-1). %str( A_&i.);

This indirect reference works for when you have multiple & in a reference, for example:

%let x_&i = &&x_&j;

But in your case you are generating the second part of your macro variable name with %eval.  When the macro processor (word scanner? tokenizer?) starts resolving &&x_%eval(&i.-1) it first sees the && and resolves it to &.  Then it sees x_.  Then it sees the % and thinks it hit the end of macro variable reference because it doesn't know %eval can generate part of the macro variable name.  It knows there is still an & left in the token from the first pass, so it makes a second pass and tries to resolve &x_.  It can't resolve.  If you add %put _local_; to your macro, you will see you end up with the following macro variables:

 

TEMP I 6
TEMP X_1 A_1
TEMP X_2 &x_1.  A_2
TEMP X_3 &x_2.  A_3
TEMP X_4 &x_3.  A_4
TEMP X_5 &x_4.  A_5

While the %PUT statement resolves everything like you hoped, you haven't actually built the macro variables you want.

 

@PaigeMiller solved this nicely by refactoring to move the %eval out of the macro reference, avoiding the problem.

 

One option when you want to build a macro reference from macro statements is to use quoting functions to delay the resolution of the macro variables.  It gets a little ugly (but hey, it's macro : )  but you basically quote the & and then %unquote it after the %eval has done its work.  Something like:

 

%macro temp();
  %let x_1 = A_1;
  %do i= 2 %to 10;
    %let x_&i = %unquote(%nrstr(&x_)%eval(&i.-1)) A_&i;
    %put &&x_&i;
  %end;
  %put _local_ ;
%mend;
%temp()

 

BASUG is hosting free webinars Next up: Mark Keintz presenting History Carried Forward, Future Carried Back: Mixing Time Series of Differing Frequencies on May 8. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.

View solution in original post

13 REPLIES 13
PaigeMiller
Diamond | Level 26

This works for me

 

%macro temp;
%let x_1 = A_1;
%do i= 2 %to 5;
    %let i_minus_1=%eval(&i-1);
    %let x_&i = &&x_&i_minus_1 A_&i;
    %put &&x_&i;
    %end;
%mend;
%temp
--
Paige Miller
thepushkarsingh
Quartz | Level 8

ohh, so using %eval in same expression was giving warning?

VDD
Ammonite | Level 13 VDD
Ammonite | Level 13

the warnings were not the only issue in the code.

Assuming the symbol X was misspelled as x_. 

 

NOTE: Line generated by the invoked macro "TEMP".
1 data want; do i= 2 to 5; x_&i. = compress(&&x_%eval(&i.-1). A_&i.);
--
14
1 ! );
WARNING: Apparent symbolic reference I not resolved.
WARNING: Apparent symbolic reference X_ not resolved.
WARNING: Apparent symbolic reference I not resolved.
WARNING: Apparent symbolic reference I not resolved.
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The
condition was: &i.-1
WARNING: Apparent symbolic reference I not resolved.
WARNING 14-169: Assuming the symbol X was misspelled as x_.

ERROR: The macro TEMP will stop executing.

 

 

thepushkarsingh
Quartz | Level 8

Thanks for the solution.

Kurt_Bremser
Super User

Why don't you do it in a data step with call symputx, where the logic is much easier to build?

data _null_;
length string $100;
string = "";
do i = 1 to 5;
  string = catx(' ',string,'A_' !! left(put(i,best.)));
  call symputx('X_' !! left(put(i,best.)),string);
  put string=;
end;
run;

%put x_3=&x_3.;
%put x_5=&x_5.;

Note that the data step is SAS' primary tool for handling data; the macro language is for creating dynamic code, NOT for handling data.

thepushkarsingh
Quartz | Level 8
5, is not fixed depending on data it's changing and I am using that part inside a macro, but in first place I always thought that using macro facility is more efficient. I just want to use this as passing variable names, do you think data step will be more efficient?
Tom
Super User Tom
Super User

@thepushkarsingh wrote:
5, is not fixed depending on data it's changing and I am using that part inside a macro, but in first place I always thought that using macro facility is more efficient. I just want to use this as passing variable names, do you think data step will be more efficient?

You code will be clearly if you use SAS code.  Only resort to using macro logic when normal SAS code cannot do the job.

 

So if you need to reference 5 variables then just use a variable list.

result=mean(of A1-A5);

So to make that dynamic you could replace just the 5 with a macro variable.

%let n=5;
....
result=mean(of A1-A&n);

But even if did need to generate your list of macro variables if you have a macro that is generating code already. say a data step or a proc step, then using a data step to generate macro variables will probably make it easier to create your code. It is much easier to debug a data step than a macro.

thepushkarsingh
Quartz | Level 8

Thanks for the tips.

Kurt_Bremser
Super User

The macro loop is executed once, and the loop in the data step is executed once, so there's no difference there.

 

Doing a calculation in a macro may be more efficient when your code looks like this:

data want;
set sashelp.class;
age = age * 12345 ** (-2);
run;

Now, if you change that to this:

data want2;
set sashelp.class;
age = age * %sysevalf(12345 ** (-2));
run;

the macro preprocessor will evaluate the fixed part of the equation once when the data step is compiled, instead of the data step doing the calculation for every observation.

 

But the bottom line is to always keep data handling and manipulation in Base SAS code, where the logic is easier to code and understand. Optimizations like the one above make sense only when there's a considerable performance gain.

thepushkarsingh
Quartz | Level 8

Thanks for nice explanation. I will keep these in my mind.

Quentin
Super User

Good question!

 

The problem is that you're trying to use && to create an indirect macro reference:

%let x_&i. = &&x_%eval(&i.-1). %str( A_&i.);

This indirect reference works for when you have multiple & in a reference, for example:

%let x_&i = &&x_&j;

But in your case you are generating the second part of your macro variable name with %eval.  When the macro processor (word scanner? tokenizer?) starts resolving &&x_%eval(&i.-1) it first sees the && and resolves it to &.  Then it sees x_.  Then it sees the % and thinks it hit the end of macro variable reference because it doesn't know %eval can generate part of the macro variable name.  It knows there is still an & left in the token from the first pass, so it makes a second pass and tries to resolve &x_.  It can't resolve.  If you add %put _local_; to your macro, you will see you end up with the following macro variables:

 

TEMP I 6
TEMP X_1 A_1
TEMP X_2 &x_1.  A_2
TEMP X_3 &x_2.  A_3
TEMP X_4 &x_3.  A_4
TEMP X_5 &x_4.  A_5

While the %PUT statement resolves everything like you hoped, you haven't actually built the macro variables you want.

 

@PaigeMiller solved this nicely by refactoring to move the %eval out of the macro reference, avoiding the problem.

 

One option when you want to build a macro reference from macro statements is to use quoting functions to delay the resolution of the macro variables.  It gets a little ugly (but hey, it's macro : )  but you basically quote the & and then %unquote it after the %eval has done its work.  Something like:

 

%macro temp();
  %let x_1 = A_1;
  %do i= 2 %to 10;
    %let x_&i = %unquote(%nrstr(&x_)%eval(&i.-1)) A_&i;
    %put &&x_&i;
  %end;
  %put _local_ ;
%mend;
%temp()

 

BASUG is hosting free webinars Next up: Mark Keintz presenting History Carried Forward, Future Carried Back: Mixing Time Series of Differing Frequencies on May 8. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
Astounding
PROC Star

It's a bit of a black box, where we don't understand what is happening 100%.  But these results suggest that you should be able to resolve the problem by forcing the macro processor to make an extra pass through the expression:

 

%let x_&i = &&&&x_%eval(&i.-1). A_&i;

 

thepushkarsingh
Quartz | Level 8

Thank you very much, this makes a lot of things clear.

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

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
  • 13 replies
  • 2698 views
  • 7 likes
  • 7 in conversation