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

Hello,

 

Just trying to learn loops, can't figure out why it keeps giving me this error even though there are no unclosed loops. Thanks in advance for pointing my mistake out.

 

data main;
infile datalines; 
input acc $19.;
obs=_n_; 
datalines; 
12344567969705
56754846834684
12344567969715
56754846834694
56754846834696
; 
run;

proc rank data=main out=main1 groups=2;                               
     var obs;                                                          
     ranks rank;                                                      
  run;

%macro create (n);
  data main_&n.;
  set main1;
  if rank = &n. then output;
run;
%mend;

data _null_;
i=0;
do until (i=2);
%create(i);
i=i+1;
end;
run;

 

Log:

NOTE: The SAS System stopped processing this step because of errors.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds


NOTE: Line generated by the invoked macro "CREATE".
2174 data main_&n.; set main1; if rank = &n. then output; run;
-
117

ERROR 117-185: There was 1 unclosed DO block.

NOTE: Variable i is uninitialized.
NOTE: There were 5 observations read from the data set WORK.MAIN1.
NOTE: The data set WORK.MAIN_I has 0 observations and 4 variables.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.01 seconds

2175 i=i+1;
-
180

ERROR 180-322: Statement is not valid or it is used out of proper order.

2176 end;
---
180

ERROR 180-322: Statement is not valid or it is used out of proper order.

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

That's great, except if usually does not make any sense to create a macro that uses "magical" macro variables that appear from no where when they are clearly parameters.  Pass the value when calling the macro.

call execute (cats('%nrstr(%create)(n=',i,')'));

View solution in original post

9 REPLIES 9
Kurt_Bremser
Super User

You cannot nest data steps, but that is what your macro does. The "data" statement created by the macro ends the "outer" step before the "end" statement could be compiled.

SASKiwi
PROC Star

If you are just learning loops don't complicate things by adding macros. The way you have written it you are trying to run one DATA step inside another which is not allowed in SAS. Remove the macro call and add a simple SAS statement:

 

data _null_;
i=0;
do until (i=2);
%create(i); * <== remove this;
put i=;  <== add this;
i=i+1;
end;
run;
PaigeMiller
Diamond | Level 26

@AJ_Brien: Adding to all the above good comments

 

Macros perform text substitution — when the program runs, the macro elements of the code are replaced by the values of the macro variables or the values of the macro, and once this text substitution happens, you MUST have valid legal working SAS code, which you do not have.

 

When writing macros, or using macro variables, you first need to write some code that works for one or two instances WITHOUT macros and WITHOUT macro variables. If you had done that, you would know the problem isn't the macros, the problem is that your code WITHOUT macros and WITHOUT macro variables has errors. Once you have non-macro code without errors, then you can go ahead and insert macros or macro variables, and have a reasonable likelihood that the code will work with macros and with macro variables.

--
Paige Miller
Astounding
PROC Star

It looks like the problem has been well identified, but the solutions are not really sufficient. 

 

Change two pieces to your program.  First, get rid of the macro parameter N on %create, so it just reads:

 

%macro create;

 

The definition of the macro does not otherwise change, and should still reference &n.  However, &n will be set using different methods.

 

Then address the problem DATA step:

data _null_;
i=0;
do until (i=2);
%create(i);
i=i+1;
end;
run;

Replace that with:

data _null_;
i=0;
do until (i=2);
   call symputx('n', i) ;
   call execute ('%create');
   i=i+1;
end;
run;

There are tricky reasons why this will work, that require good understanding of CALL EXECUTE.

 

Tom
Super User Tom
Super User

That's great, except if usually does not make any sense to create a macro that uses "magical" macro variables that appear from no where when they are clearly parameters.  Pass the value when calling the macro.

call execute (cats('%nrstr(%create)(n=',i,')'));
DonH
Lapis Lazuli | Level 10

For those of you familiar with CALL EXECUTE, I would suggest that you look into the DOSUBL routine as an alternative to EXECUTE. EXECUTE generates code which runs after the DATA step finishes. DOSUBL runs the code immediately.

AJ_Brien
Quartz | Level 8
this works perfectly, thank you!
DonH
Lapis Lazuli | Level 10

All of the comment above are spot-on as to the issue - the fact that macro is a code generator.

It looks like the intent is to use a data step to loop thru the distinct values (instead of a macro %do loop) and create a separate data set for each value.

If so, a more direct approach would be a macro like this:


%macro split(data=
            ,var=
            );

  proc sql noprint;
   /* create an "array" (value1, value2,...) of distinct values */
   select distinct &var into:value1- from &data;
  quit;

  %local i;
  data %do i = 1 %to &sqlobs; main_&&value&i %end;
  ;
   set &data;
   select (&var);
      %do i = 1 %to &sqlobs; 
          when (&&value&i) output main_&&value&i;
      %end;
   end;
  run;

%mend split;

which can be called for this case as follows:

options mprint;
%split(data=main1,var=rank)

I included the mprint option so the generate code is shown in the log:

MPRINT(SPLIT):   proc sql noprint;
MPRINT(SPLIT):   select distinct rank into:value1- from main1;
MPRINT(SPLIT):   quit;
NOTE: PROCEDURE SQL used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds


MPRINT(SPLIT):   data main_0 main_1 ;
MPRINT(SPLIT):   set main1;
MPRINT(SPLIT):   select (rank);
MPRINT(SPLIT):   when (0) output main_0;
MPRINT(SPLIT):   when (1) output main_1;
MPRINT(SPLIT):   end;
MPRINT(SPLIT):   run;

NOTE: There were 5 observations read from the data set WORK.MAIN1.
NOTE: The data set WORK.MAIN_0 has 2 observations and 3 variables.
NOTE: The data set WORK.MAIN_1 has 3 observations and 3 variables.
NOTE: DATA statement used (Total process time):
      real time           0.01 seconds
      cpu time            0.01 seconds

This code is a variable of one of the example covered in the current SAS Macro Language course.

Disclaimer: this code is not intended as a bulletproof macro - just a POC.

AJ_Brien
Quartz | Level 8
I ran this and this works too, sadly I haven't reached the stage where I could use arrays. But I've made a note of it for future learning, this is great. Thank you!

SAS Innovate 2025: Call for Content

Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!

Submit your idea!

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
  • 9 replies
  • 12997 views
  • 0 likes
  • 7 in conversation