BookmarkSubscribeRSS Feed
enginemane44
Calcite | Level 5
Hi, SAS-Lers,
I have a macro that uses a %if statement to check the value of a macro variable against a *data step* variable. The problem is that the comparison seems to check the value of the macro variable (which resolves correctly) to the *name* of the data set variable (qnum in this case). Any ideas here ? I tried several approaches using call symput, call execute, %eval, - all without success.
Here is version 1 of the program:
option symbolgen mprint mlogic;

data one;
input qnum;
datalines;
1
2
3
;

proc print data = one;
title 'Test data set';
run;


%macro makevar;
%local x;
data new;
set one;
%do x = 1 %to 3;
%if &x=qunum %then %do; /* Should be TRUE for first obs of data set */
%put IF condition is true for &i ;
%end;
%end;
run;
%mend makevar;

%makevar

Part of the log for this program :
28 %makevar
MLOGIC(MAKEVAR): Beginning execution.
MLOGIC(MAKEVAR): %LOCAL X
MPRINT(MAKEVAR): data new;
MPRINT(MAKEVAR): set one;
MLOGIC(MAKEVAR): %DO loop beginning; index variable X; start value is 1; stop value is 3; by value is 1.
SYMBOLGEN: Macro variable X resolves to 1
MLOGIC(MAKEVAR): %IF condition &x=qunum is FALSE <-- HERE IS THE PROBLEM
MLOGIC(MAKEVAR): %DO loop index variable X is now 2; loop will iterate again.
SYMBOLGEN: Macro variable X resolves to 2
MLOGIC(MAKEVAR): %IF condition &x=qunum is FALSE
MLOGIC(MAKEVAR): %DO loop index variable X is now 3; loop will iterate again.
SYMBOLGEN: Macro variable X resolves to 3
MLOGIC(MAKEVAR): %IF condition &x=qunum is FALSE
MLOGIC(MAKEVAR): %DO loop index variable X is now 4; loop will not iterate again.
MPRINT(MAKEVAR): run;

So, it appears the comparison %if is making is the resolved value of &x (which is 1) to the name 'qnum'; hence, it is never true - and it should be on the first observation of the data set.

Second try using call symput and second macro variables &y:
%macro makevar;
%local x y;
data new;
set one;
%do x = 1 %to 3;
call symputx('y',qnum);
%if &x=&y %then %do; /* Should be TRUE for first obs of data set */
%put IF condition is true for &x ;
%end;
%end;
run;
%mend makevar;

%makevar

And here's the log from this program:
29 %makevar
MLOGIC(MAKEVAR): Beginning execution.
MLOGIC(MAKEVAR): %LOCAL X Y
MPRINT(MAKEVAR): data new;
MPRINT(MAKEVAR): set one;
MLOGIC(MAKEVAR): %DO loop beginning; index variable X; start value is 1; stop value is 3; by value is 1.
MPRINT(MAKEVAR): call symputx('y',qnum);
SYMBOLGEN: Macro variable X resolves to 1
SYMBOLGEN: Macro variable Y resolves to <-- HERE IS THE PROBLEM
MLOGIC(MAKEVAR): %IF condition &x=&y is FALSE
MLOGIC(MAKEVAR): %DO loop index variable X is now 2; loop will iterate again.
MPRINT(MAKEVAR): call symputx('y',qnum);
SYMBOLGEN: Macro variable X resolves to 2
SYMBOLGEN: Macro variable Y resolves to
MLOGIC(MAKEVAR): %IF condition &x=&y is FALSE
MLOGIC(MAKEVAR): %DO loop index variable X is now 3; loop will iterate again.
MPRINT(MAKEVAR): call symputx('y',qnum);
SYMBOLGEN: Macro variable X resolves to 3
SYMBOLGEN: Macro variable Y resolves to
MLOGIC(MAKEVAR): %IF condition &x=&y is FALSE
MLOGIC(MAKEVAR): %DO loop index variable X is now 4; loop will not iterate again.
MPRINT(MAKEVAR): run;


Do any of you macro experts have any idea why this is not working ? I have a feeling it involves the timing of the macro compilation vs. data set compilation.

Barry Walton
Barry.Walton@millersville.edu
7 REPLIES 7
sbb
Lapis Lazuli | Level 10 sbb
Lapis Lazuli | Level 10
You are attempting to mix SAS macro language (code and macro variable resolution) with SAS DATA step execution logic - the two are complementary however they cannot be interwoven, normally.

The macro logic / execution is resolved at SAS compilation time, evident when you have coded the following SAS OPTIONS statement for the most diagnostic output:

OPTIONS SOURCE SOURCE2 MACROGEN SYMBOLGEN MLOGIC MPRINT;

So, you will need/want to consider this challenge when developing your SAS application program/solution.

Scott Barry
SBBWorks, Inc.
SPR
Quartz | Level 8 SPR
Quartz | Level 8
Hello Enginemane44,

This is a solution. Simply replace %IF with datastep if:

%macro makevar;
%local x;
data new;
set one;
%do x = 1 %to 3;
data new;
set one;
if &x = qnum then do; /* Should be TRUE for first obs of data set */
%put IF condition is true for &x ;
end;
run;

%end;
%mend makevar;

%makevar

Sincerely,
SPR
polingjw
Quartz | Level 8
In your code, because the %put statement is a macro statement, it will be executed whether or not the condition &x=qnum (specified in the data step if statement) is true. As sbb pointed out, you are still mixing macro logic with data step logic. To see this, change the condition to “if 1 = 2” and watch what happens:
[pre]

MPRINT(MAKEVAR): data new;
MPRINT(MAKEVAR): set one;
MPRINT(MAKEVAR): if 1 = 2 then do;
MLOGIC(MAKEVAR): %PUT IF condition is true for &x
IF condition is true for 1
MPRINT(MAKEVAR): end;
MPRINT(MAKEVAR): run;

NOTE: There were 3 observations read from the data set WORK.ONE.
NOTE: The data set WORK.NEW has 3 observations and 1 variables.
NOTE: DATA statement used (Total process time):
real time 0.01 seconds
cpu time 0.01 seconds


MLOGIC(MAKEVAR): %DO loop index variable X is now 2; loop will iterate again.
MPRINT(MAKEVAR): data new;
MPRINT(MAKEVAR): set one;
MPRINT(MAKEVAR): if 1 = 2 then do;
MLOGIC(MAKEVAR): %PUT IF condition is true for &x
IF condition is true for 2
MPRINT(MAKEVAR): end;
MPRINT(MAKEVAR): run;

NOTE: There were 3 observations read from the data set WORK.ONE.
NOTE: The data set WORK.NEW has 3 observations and 1 variables.
NOTE: DATA statement used (Total process time):
real time 0.01 seconds
cpu time 0.01 seconds


MLOGIC(MAKEVAR): %DO loop index variable X is now 3; loop will iterate again.
MPRINT(MAKEVAR): data new;
MPRINT(MAKEVAR): set one;
MPRINT(MAKEVAR): if 1 = 2 then do;
MLOGIC(MAKEVAR): %PUT IF condition is true for &x
IF condition is true for 3
MPRINT(MAKEVAR): end;
MPRINT(MAKEVAR): run;

NOTE: There were 3 observations read from the data set WORK.ONE.
NOTE: The data set WORK.NEW has 3 observations and 1 variables.
NOTE: DATA statement used (Total process time):
real time 0.01 seconds
cpu time 0.00 seconds


MLOGIC(MAKEVAR): %DO loop index variable X is now 4; loop will not iterate again.
MLOGIC(MAKEVAR): Ending execution.

[/pre]


Here is an alternative which reads only one observation from the dataset, using call symput to create a corresponding macro variable, and then performs the macro logic after the data step has finished executing:
[pre]
data one;
input qnum;
datalines;
1
2
3
5
;

%macro makevar;

%local x DSID NOBS DSIDC;

/*** OBTAIN NUMBER OF OBSERVATIONS ***/
%LET DSID=%SYSFUNC(OPEN(ONE));
%LET NOBS=%SYSFUNC(ATTRN(&DSID,NLOBS));
%LET DSIDC=%SYSFUNC(CLOSE(&DSID));

%DO X=1 %TO &NOBS;
data _NULL_;
OBNUM=&X;
set one POINT=OBNUM;
CALL SYMPUTX("QNUM", QNUM);
STOP;
RUN;
%if &x=&qnum %then %do; /* Should be TRUE for first obs of data set */
%put IF condition is true for &X ;
%end;
%else %do;
%put IF condition is not true for &X.. QNUM = &QNUM;
%end;
%end;

%mend makevar;
%MAKEVAR
[/pre]
SPR
Quartz | Level 8 SPR
Quartz | Level 8
It does not matter. The condition (if &x = qnum then do;) works correctly. To test it simply replace

%put IF condition is true for &x ;

with

put qnum= "IF condition is true for &x" ;

SPR
polingjw
Quartz | Level 8
Yes, if you use a put statement instead of %put, the program will work. I was just pointing out that you cannot use a data step if statement to conditionally execute a %put macro statement.

The example I previously posted above was to illustrate a macro that “uses a %if statement to check the value of a macro variable against a data step variable.” However, it sounds like the %if statement is not needed in this example at all.
enginemane44
Calcite | Level 5
Hello all,

SPR's suggestion to change the %if to a regular if did the job ! Thank you for the help !
One general question - can someone direct me to some online info/papers that deal with this topic ? I took the 'SAS Macro Language 2' course in June, but we didn't go into this topic in detail (it was still a worthwhile course). I've searched various archives, but couldn't come up with very much.
Again, thanks to SPR - your solution worked.

Barry Walton
Barry.Walton@millersville.edu
SPR
Quartz | Level 8 SPR
Quartz | Level 8
Hello Enginemane44,

As far as I know, the problem in your program is because %if condition is resolved on compilation stage when variable values are not accessible yet.

This is an extraction from SAS help topic "%IF-%THEN/%ELSE Statement":

Comparisons


Although they look similar, the %IF-%THEN/%ELSE statement and the IF-THEN/ELSE statement belong to two different languages. In general, %IF-%THEN/%ELSE statement, which is part of the SAS macro language, conditionally generates text. However, the IF-THEN/ELSE statement, which is part of the SAS language, conditionally executes SAS statements during DATA step execution.

The expression that is the condition for the %IF-%THEN/%ELSE statement can contain only operands that are constant text or text expressions that generate text. However, the expression that is the condition for the IF-THEN/ELSE statement can contain only operands that are DATA step variables, character constants, numeric constants, or date and time constants.

When the %IF-%THEN/%ELSE statement generates text that is part of a DATA step, it is compiled by the DATA step compiler and executed. On the other hand, when the IF-THEN/ELSE statement executes in a DATA step, any text generated by the macro facility has been resolved, tokenized, and compiled. No macro language elements exist in the compiled code. "Example 1: Contrasting the %IF-%THEN/%ELSE Statement with the IF-THEN/ELSE Statement" illustrates this difference.

For more information, see SAS Programs and Macro Processing, and Macro Expressions.

Sincerely,
SPR

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

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
  • 7 replies
  • 1669 views
  • 0 likes
  • 4 in conversation