|
Hi, Ian and mavens and gurus,
Wow! As always, I just learn a lot from Ian's thoughtful code and
comments.
His final comment about whether we should use an extra parameter (X)
got me thinking. I thought it was a good idea. And after some time, I
came up with a little bit of generalization of the loop_through_months
macro as in the below. The basic idea is: if we have to use an extra
parameter, then let's make the full use of it.
Given the fact that my co-workers do not really appreciate it when I
try to think :-), I am not really confident if this is a good idea
(hopefully) or a terribly bad one. I really appreciate your comments.
Thank you.
Cheers,
Chang
macro --------------------------------------------------------
%macro forEach(unit=, from=, to=, invoke=);
%local &unit. start end next;
%let unit = %lowcase(%trim(%left(&unit.)));
%if %index(year month day, &unit.) %then %do;
%let start = %sysfunc(intnx(&unit.,
%sysfunc(inputn(&from.,date9.)),0));
%let end = %sysfunc(intnx(&unit.,
%sysfunc(inputn(&to. ,date9.)),0));
%let next = %nrstr(%sysfunc(intnx(&unit.,&&&unit..,0,e)));
%end; %else %if &unit.=number %then %do;
%let start = &from.;
%let end = &to.;
%let next = %nrstr(&&&unit..);
%end;
%do &unit. = %unquote(&start.) %to %unquote(&end.);
%unquote(&invoke.);
%let &unit. = %unquote(&next.);
%end;
%mend forEach;
--------------------------------------------------------------
usage examples (log outputs in comments) ---------------------
%macro test(year=);
%put ***year=&year.***;
%mend test;
%forEach( unit=year,
from=20mar2000,
to=23feb2002,
invoke=%nrstr(%test(year=%sysfunc(putn(&year.,year4.))))
);
/*
***year=2000***
***year=2001***
***year=2002***
*/
%macro test(month=);
%put ***month=&month.***;
%mend test;
%forEach( unit=month,
from=01dec1999,
to=10feb2000,
invoke=%nrstr(%test(month=%sysfunc(putn(&month.,yymmn6.))))
);
/*
***month=199912***
***month=200001***
***month=200002***
*/
%macro test(day=);
%put ***day=&day.***;
%mend test;
%forEach( unit=day,
from=28feb2000,
to=02mar2000,
invoke=%nrstr(%test(day=%sysfunc(putn(&day.,worddatx.))))
);
/*
***day=28 February 2000***
***day=29 February 2000***
***day=1 March 2000***
***day=2 March 2000***
*/
%macro test(number=);
%put ***number=&number.***;
%mend test;
%forEach( unit=number,
from=1,
to=3,
invoke=%nrstr(%test(number=&number))
);
/*
***number=1***
***number=2***
***number=3***
*/
--------------------------------------------------------------
WHITLOI1@WESTAT.COM (Ian Whitlock) wrote in message news:<569DFDF32A05FC41A33DAA026649A18B08007F@REMAILW2K2>...
> Doug,
>
> I would simplify and speed things up with SAS dates. Following Dennis'
> suggestion and using
>
> %macro loop_through_months(startmon=, endmon=, sas_code_to_loop=);
>
> %local mon ;
>
> %do mon = %sysfunc(inputn(01&startmon,date9.))
> %to %sysfunc(inputn(01&endmon,date9.)) ;
>
> %unquote (&sas_code_to_loop)(mon=&mon)
>
> %let mon = %sysfunc(intnx(month,&mon,0,e)) ;
> %end;
> %mend loop_through_months;
>
> %macro test_macro(mon=);
> %put ====> test_macro executing,
> curr_yyyymm=%sysfunc(putn(&mon,yymmn6.)) ;
> %mend test_macro ;
>
>
> %loop_through_months(startmon=dec2002,
> endmon=feb2003,
> sas_code_to_loop=%nrstr(%test_macro))
>
> Some comments are in order. With all those function calls, is it really
> faster? Yes functions have been implemented very efficiently in comparison
> with macro code. Why use the end of the month in the incrementation?
> Because the iterative loop will add 1 bringing the date to the first of the
> month. Why not use %BY 0? Because some Institute programmer checks your
> code and refuses to allow %BY 0.
>
> Should one have the hidden parameter MON? It is questionable. It could be
> left out, but my conscience won't let me call macros without passing
> parameters. Why didn't I simply make the call
>
> %loop_through_months(startmon=dec2002,
> endmon=feb2003,
> sas_code_to_loop=%nrstr(%test_macro(mon=&mon))
>
> I didn't want to burden your call with my conscience. Actually this form
> may casue more problems than it is worth. I probably would change the call
> the design to
>
> %macro loop_through_months(x= ,startmon=, endmon=, sas_code_to_loop=);
>
> %local &x ;
>
> %do &x = %sysfunc(inputn(01&startmon,date9.))
> %to %sysfunc(inputn(01&endmon,date9.)) ;
>
>
> %unquote (&sas_code_to_loop)
>
> %let &x = %sysfunc(intnx(month,&&&x,0,e)) ;
> %end;
> %mend loop_through_months;
>
> %macro test_macro (mon=mon);
> %put ====> test_macro executing,
> curr_yyyymm=%sysfunc(putn(&mon,yymmn6.)) ;
> %mend test_macro ;
>
> options nomlogic;
> %loop_through_months(x=mon,
> startmon=dec2002,
> endmon=feb2003,
> sas_code_to_loop=%nrstr(%test_macro(mon=&mon)))
>
> Here the parameter, X, allows the user to name the index and then to pass
> the required value. Is it worth it? I still am not sure, but it allows the
> user to name the index and to pass the value of that index as a parameter.
>
>
> IanWhitlock@westat.com
> -----Original Message-----
> From: Doug Zirbel [mailto:doug_zirbel@HOTMAIL.COM]
> Sent: Wednesday, September 24, 2003 12:56 PM
> To: SAS-L@LISTSERV.UGA.EDU
> Subject: Nested macro question for mavens and gurus
>
>
> I wrote a macro which loops through "months", each iteration incrementing a
> YYYYMM macro variable by 1 month. I want to pass that &YYYYMM to another
> macro nested within the loop, but it of course is not cooperating.
>
> So how do I pass a macro variable created with a %LET to a macro as its
> macro parameter?
>
> Here's the code:
>
> options nocenter mprint mlogic;
> %global curr_yyyymm;
> %let curr_yyyymm=;
>
> /*******************************************/
> /* This is the test macro (with a parameter) to
> /* be nested into the macro loop in the next
> /* macro.
> /*******************************************/
> %macro test_macro;
> %put ====> test_macro executing, curr_yyyymm=&curr_yyyymm; %mend;
>
>
> /*******************************************************/
> /* This is the macro that loops through
> /* "months" creating a new macro var "curr_yyyymm"
> /* which is incremented by 1 month each iteration.
> /* Right after it is incremented, it is to pass
> /* that macro var to the nested macro.
> /*******************************************************/
> %macro loop_through_months(start_yyyymm=, end_yyyymm=, sas_code_to_loop=);
>
> %let curr_yyyymm = &start_yyyymm;
>
> /**********************************************
> /* Loop, from starting YYYYMM to ending YYYYMM
> /* incrementing at the bottom. "DO UNTIL"
> /* does its T/F test at the bottom of the loop.
> /**********************************************/
> %do %until (&curr_yyyymm > &end_yyyymm);
> %put curr_yyyymm=&curr_yyyymm;
> /*******************************************/
> /* This macro var holds the test_macro to be
> /* passed to the loop and to then be
> /* executed before the next loop interation.
> /*******************************************/
> &&sas_code_to_loop;
>
> /*******************************************/
> /* Increment from December (12) to January (01)
> /* if necessary after calling nested macro
> /*******************************************/
> %if %substr(&curr_yyyymm,5,2) = 12 %then %do;
> %let curr_yyyy=%eval(%substr(&curr_yyyymm,1,4) + 1);
> %let curr_mm = 01;
> %let curr_yyyymm=&curr_yyyy.&curr_mm;
> %end;
>
> /*******************************************/
> /* Increment from one month # to the next #
> /* after calling nested macro
> /*******************************************/
> %else %do;
> %let curr_yyyymm=%eval(&curr_yyyymm + 1);
> %end;
>
> %end;
> %mend loop_through_months;
>
> %loop_through_months(start_yyyymm=200212,
> end_yyyymm=200302,
> sas_code_to_loop=%test_macro);
|