Macro Do-Loop

From sasCommunity
Jump to: navigation, search

This article describes a macro which is a follow-up to User:Ron.Fehd.macro.maven and User:ArtCarpenter List_Processing_Basics_Creating_and_Using_Lists_of_Macro_Variables

Author: Ronald_J._Fehd 2012

Programs.v2

Macro Do_Loop

NOTE! This code was updated on 2012-06-27 with the named parameter UpperBound in order to handle arrays of macro variables correctly.

 /*    name: <UNC>\SAS-site\macros\do_loop.sas
description: macro function to return tokens inside a loop
    purpose: list processing routine
usage:
*with macro arrays;
%let C1 = c1;
%let C2 = c.2;
%let C3 = C+3!;
%let C4 = 4.C*4;
%put note:
%do_loop(arrays = c
        ,text = %nrstr
        (&Item1.=&&&Item1.&I)
        ,upperbound = 4
        );
*with macro variable lists;
%let ListA = a1 b2 c3;
%put note:
%do_loop(list   = ListA
        ,text   = %nrstr
        (item&I = %scan(&ListA,&I,&Delimiter))
        );
NOTES:
* text may be any of:
  * tokens
  * macro calls
  * statements
author: Ronald J. Fehd 2012
RJF 6/27/2012 polishing for sas.wiki, changed parameter names
for tests and examples see:
http://www.sascommunity.org/wiki/Macro_Do-Loop
for commentary, compare and contrast see:
http://www.sascommunity.org/wiki/Passing_Values
*************/
%MACRO do_loop
        (arrays    = /* a: must have a1 a2 ... a.&upperbound */
        ,delimiter = %str( )
        ,elements  = item /* element or text or var ... */
        ,list      = /* item.1 item.2 ... item.N */
        ,text      = /* MUST be enclosed in %nrstr()
        ,upperbound= /* n.items of &array == dim(&array) */
        ,testing   = 0
        )
/des = 'site: do.loop for arrays or lists'
 /* ** store source /* */;
%let Testing =   &Testing
              or %sysfunc(getoption(mprint)) eq MPRINT;
 
%if &List ne  %then
    %let UpperBound=%eval(%sysfunc(countc(&&&List,%str(&Delimiter)))+1);
 
%if &Testing %then %do;
    %if &Arrays ne  %then
        %put note.Test: &SysMacroName has arrays=&Arrays;
    %else
        %put note.Test: &SysMacroName has list=&&&List;
    %put note.Test: &SysMacroName UpperBound=&UpperBound;
    %end;
 
%if &Arrays ne %then %do;
    %do I = 1 %to %eval(%sysfunc(countc(&Arrays,%str(&Delimiter)))+1);
        %local &Elements.&I;
        %Let   &Elements.&I = %scan(&Arrays,&I,&Delimiter);
    %if &Testing %then
      %put noteTest: &SysMacroName returns &Elements.&I= &&&Elements.&I;
        %end;
    %end;
%do I = 1 %to &UpperBound;
    %unquote(&Text.)
    %end;
%mend do_loop;

Macro Do_Loop-test-lists

 /*    name: <UNC>\SAS-site\sas-macro-tests\do_loop_test-list.sas
description: test program for macro do_loop
    purpose: demo of list processing routine
author: RJF2 5/4/2012
*************/
options mprint;
 
* demo.1: return tokens
          note call is within put statement
          <put note (text.from.macro) semicolon>;
 
%let ListA = a1 b2 c3;
 
%put note:
%do_loop(list  = ListA
        ,text  = %nrstr
        (item&I=%scan(&ListA,&I,%str( )))
        );
 
 
* demo.1.2 return notes
           note call is.a complete put statement:
           <put note item=... semicolon>;
 
%let ListB = B1+Bb2+BbB3;
 
%do_loop(list      = ListB
        ,delimiter = +
        ,text      = %nrstr
        (%put note: item&I=%scan(&ListB,&I,&Delimiter);)
        );
 
 
%let ListB = The quick brown fox jumped over the lazy dog;
 
%do_loop(list      = ListB
        ,delimiter = e
        ,text      = %nrstr
        (%put note: item&I=%scan(&ListB,&I,&Delimiter);)
        )
 
%do_loop(list      = ListB
        ,delimiter = t
        ,text      = %nrstr
        (%put note: item&I=%scan(&ListB,&I,&Delimiter);)
        )
 
 
* demo.2: return macro call from two lists;
 
%let ListValue = C1 * C 2 c * Cc+3!cC;
%let ListCount = 1  *  2.2 * 3.33;
 
%Macro do_this(value=,count=);
%Put note: &SysMacroName returns:;
%Put note: value=&Value;
%Put note: count=&Count;
%mend;
%do_loop(list      = ListValue
        ,delimiter = *
        ,text      = %nrstr
        (%do_this(value=%scan(&ListValue,&I,%str(*))
                 ,count=%scan(&ListCount,&I,%str(*))
                 )
        )
        )
*note: no ending semicolon;

Log:

14         %let ListA = a1 b2 c3;
15         
16         %put note:
17         %do_loop(list  = ListA
18                 ,text  = %nrstr
19                 (item&I=%scan(&ListA,&I,%str( )))
20                 );
note.Test: DO_LOOP has list=a1 b2 c3
note.Test: DO_LOOP calculates UpperBound=3
note: item1=a1     item2=b2     item3=c3
21         
22         
23         * demo.1.2 return notes
24                    note call is.a complete put statement:
25                    <put note item=... semicolon>;
26         
27         %let ListB = B1+Bb2+BbB3;
28         
29         %do_loop(list      = ListB
30                 ,delimiter = +
31                 ,text      = %nrstr
32                 (%put note: item&I=%scan(&ListB,&I,&Delimiter);)
33                 );
note.Test: DO_LOOP has list=B1+Bb2+BbB3
note.Test: DO_LOOP calculates UpperBound=3
note: item1=B1
note: item2=Bb2
note: item3=BbB3
34         
35         
36         %let ListB = The quick brown fox jumped over the lazy dog;
37         
38         %do_loop(list      = ListB
39                 ,delimiter = e
40                 ,text      = %nrstr
41                 (%put note: item&I=%scan(&ListB,&I,&Delimiter);)
42                 )
note.Test: DO_LOOP has list=The quick brown fox jumped over the lazy dog
note.Test: DO_LOOP calculates UpperBound=5
note: item1=Th
note: item2= quick brown fox jump
note: item3=d ov
note: item4=r th
note: item5= lazy dog
43         
44         %do_loop(list      = ListB
45                 ,delimiter = t
46                 ,text      = %nrstr
47                 (%put note: item&I=%scan(&ListB,&I,&Delimiter);)
48                 )
note.Test: DO_LOOP has list=The quick brown fox jumped over the lazy dog
note.Test: DO_LOOP calculates UpperBound=2
note: item1=The quick brown fox jumped over
note: item2=he lazy dog
49         
50         
51         * demo.2: return macro call from two lists;
52         
53         %let ListValue = C1 * C 2 c * Cc+3!cC;
54         %let ListCount = 1  *  2.2 * 3.33;
55         
56         %Macro do_this(value=,count=);
57         %Put note: &SysMacroName returns:;
58         %Put note: value=&Value;
59         %Put note: count=&Count;
60         %mend;
61         %do_loop(list      = ListValue
62                 ,delimiter = *
63                 ,text      = %nrstr
64                 (%do_this(value=%scan(&ListValue,&I,%str(*))
65                          ,count=%scan(&ListCount,&I,%str(*))
66                          )
67                 )
68                 )
note.Test: DO_LOOP has list=C1 * C 2 c * Cc+3!cC
note.Test: DO_LOOP calculates UpperBound=3
note: DO_THIS returns:
note: value=C1
note: count=1
note: DO_THIS returns:
note: value=C 2 c
note: count=2.2
note: DO_THIS returns:
note: value=Cc+3!cC
note: count=3.33
69         *note: no ending semicolon;


Macro Do_Loop-test-macro-arrays

 /*    name: <UNC>\SAS-site\macro-tests\do_loop_test-macro-arrays.sas
description: test program for macro do_loop
    purpose: demo of list processing routine
author: RJF 5/4/2012
RJF 5/28/2012 updated array C with item.4
              added upperbound to calls
*************/
options mprint;
 
* initialize: make macro arrays;
%let A1 = a1;
%let A2 = .a2;
%let A3 = .a3.;
 
%let B1 = b1;
%let B2 = .b2;
%let B3 = .b3.;
 
%let C1 = c1;
%let C2 = c.2;
%let C3 = C+3!;
%let C4 = 4.C*4;
 
* demo.1: return tokens
          note call is with statment <put note semicolon>;
 
%put note:
%do_loop(arrays = a b c
        ,text   = %nrstr
        (&Item1.=&&&Item1.&I &Item2.=&&&Item2.&I &Item3.=&&&Item3.&I)
        ,upperbound = 3
        );
 
* demo.1.2 return notes statement
           note text is complete statement: <put ... semicolon>;
%do_loop(arrays    = a b c
        ,elements  = var
        ,text = %nrstr
      (%put note &Var1.=&&&Var1.&I &Var2.=&&&Var2.&I &Var3.=&&&Var3.&I;)
        ,upperbound = 3
        )
 
* demo.1.3 return notes statement
           note text is complete statement: <put ... semicolon>;
%do_loop(arrays    = c
        ,elements  = var
        ,text = %nrstr
      (%put note &Var1.=&&&Var1.&I ;)
        ,upperbound = 4
        )
 
 
* demo.2: return macro call: <do_this(parm1=value1,...,parmN=valueN)>;
 
%Macro do_this(a=,b=,c=);
%Put note &SysMacroName returns:;
%Put note A=&A;
%Put note B=&B;
%Put note C=&C;
%mend;
%do_loop(arrays   = a b c
        ,elements = parm
        ,text     = %nrstr
(%do_this(&parm1.=&&&parm1.&I,&parm2.=&&&parm2.&I,&parm3.=&&&parm3.&I))
        ,upperbound = 3
        )
* note: no ending semicolon;

Log:

25         %put note:
26         %do_loop(arrays = a b c
27                 ,text   = %nrstr
28                 (&Item1.=&&&Item1.&I &Item2.=&&&Item2.&I &Item3.=&&&Item3.&I)
29                 ,upperbound = 3
30                 );
note.Test: DO_LOOP has arrays=a b c
note.Test: DO_LOOP UpperBound=3
noteTest: DO_LOOP returns item1= a
noteTest: DO_LOOP returns item2= b
noteTest: DO_LOOP returns item3= c
note: a=a1 b=b1 c=c1     a=.a2 b=.b2 c=c.2     a=.a3. b=.b3. c=C+3!
31         
32         * demo.1.2 return notes statement
33                    note text is complete statement: <put ... semicolon>;
34         %do_loop(arrays    = a b c
35                 ,elements  = var
36                 ,text = %nrstr
37               (%put note &Var1.=&&&Var1.&I &Var2.=&&&Var2.&I &Var3.=&&&Var3.&I;)
38                 ,upperbound = 3
39                 )
note.Test: DO_LOOP has arrays=a b c
note.Test: DO_LOOP UpperBound=3
noteTest: DO_LOOP returns var1= a
noteTest: DO_LOOP returns var2= b
noteTest: DO_LOOP returns var3= c
note a=a1 b=b1 c=c1
note a=.a2 b=.b2 c=c.2
note a=.a3. b=.b3. c=C+3!
40         
41         * demo.1.3 return notes statement
42                    note text is complete statement: <put ... semicolon>;
43         %do_loop(arrays    = c
44                 ,elements  = var
45                 ,text = %nrstr
46               (%put note &Var1.=&&&Var1.&I ;)
47                 ,upperbound = 4
48                 )
note.Test: DO_LOOP has arrays=c
note.Test: DO_LOOP UpperBound=4
noteTest: DO_LOOP returns var1= c
note c=c1
note c=c.2
note c=C+3!
note c=4.C*4
49         
50         
51         * demo.2: return macro call: <do_this(parm1=value1,...,parmN=valueN)>;
52         
53         %Macro do_this(a=,b=,c=);
54         %Put note &SysMacroName returns:;
55         %Put note A=&A;
56         %Put note B=&B;
57         %Put note C=&C;
58         %mend;
59         %do_loop(arrays   = a b c
60                 ,elements = parm
61                 ,text     = %nrstr
62         (%do_this(&parm1.=&&&parm1.&I,&parm2.=&&&parm2.&I,&parm3.=&&&parm3.&I))
63                 ,upperbound = 3
64                 )
note.Test: DO_LOOP has arrays=a b c
note.Test: DO_LOOP UpperBound=3
noteTest: DO_LOOP returns parm1= a
noteTest: DO_LOOP returns parm2= b
noteTest: DO_LOOP returns parm3= c
note DO_THIS returns:
note A=a1
note B=b1
note C=c1
note DO_THIS returns:
note A=.a2
note B=.b2
note C=c.2
note DO_THIS returns:
note A=.a3.
note B=.b3.
note C=C+3!
65         * note: no ending semicolon;

Programs.v1

Macro Do_Loop

 /*    name: <UNC>\SAS-site\macros\do_loop.sas
description: macro function to return tokens inside a loop
    purpose: list processing routine
usage:
%do_loop(vars = a b c
        ,dim = 3
        ,text = %nrstr
        (&Var1.=&&&Var1.&I &Var2.=&&&Var2.&I &Var3.=&&&Var3.&I)
        )
NOTES:
* text may be any of:
  * tokens
  * macro calls
  * statements
author: RJF2 5/4/2012 
*************/    
%MACRO do_loop
        (vars=
        ,dim=
        ,text=
        );
%do I = 1 %to %sysfunc(countc(&Vars.,%str( )))+1;
    %local Var&I.;
    %Let   Var&I. = %scan(&Vars.,&I.,%str( ));
    %end;
%do I = 1 %to &Dim.;
    %unquote(&Text.)    
    %end;
%mend;

Macro Do_Loop-Test

 /*    name: <UNC>\SAS-site\sas-macro-tests\do_loop_test.sas
description: test program for macro do_loop
    purpose: demo of list processing routine
author: RJF2 5/4/2012 
*************/    
 
* initialize: make macro arrays; 
%let A1 = a1;
%let B1 = b1;
%let C1 = c1;
%let A2 = .a2;
%let B2 = .b2;
%let C2 = .c2;
%let A3 = .a3.;
%let B3 = .b3.;
%let C3 = .c3.;
 
 
* demo.1: return tokens;
 
%put note:
%do_loop(vars = a b c
        ,dim = 3
        ,text = %nrstr
        (&Var1.=&&&Var1.&I &Var2.=&&&Var2.&I &Var3.=&&&Var3.&I)
        );
 
 
* demo.2: return macro call;
 
%Macro do_this(a=,b=,c=);
%Put note &SysMacroName returns:;
%Put note a=&A;
%Put note B=&B;
%Put note C=&C;
%mend;
%do_loop(vars = a b c
        ,dim = 3
        ,text = %nrstr
        (%do_this(&Var1.=&&&Var1.&I, &Var2.=&&&Var2.&I, &Var3.=&&&Var3.&I))
        )

Log:

12         %put note:
13         %do_loop(vars = a b c
14                 ,dim = 3
15                 ,text = %nrstr
16                 (&Var1.=&&&Var1.&I &Var2.=&&&Var2.&I &Var3.=&&&Var3.&I)
17                 );
note: a=a1 b=b1 c=c1     a=.a2 b=.b2 c=.c2     a=.a3. b=.b3. c=.c3.
18         %Macro do_this(a=,b=,c=);
19         %Put note &SysMacroName returns:;
20         %Put note a=&A;
21         %Put note B=&B;
22         %Put note C=&C;
23         %mend;
24         %do_loop(vars = a b c
25                 ,dim = 3
26                 ,text = %nrstr
27                 (%do_this(&Var1.=&&&Var1.&I, &Var2.=&&&Var2.&I, &Var3.=&&&Var3.&I))
28                 )
note DO_THIS returns:
note a=a1
note B=b1
note C=c1
note DO_THIS returns:
note a=.a2
note B=.b2
note C=.c2
note DO_THIS returns:
note a=.a3.
note B=.b3.
note C=.c3.

References


--Ronald_J._Fehd macro.maven == the radical programmer 11:47, 4 May 2012 (EDT)