LISTSERV at the University of Georgia
Menubar Imagemap
Home Browse Manage Request Manuals Register
Previous (more recent) messageNext (less recent) messagePrevious (more recent) in topicNext (less recent) in topicPrevious (more recent) by same authorNext (less recent) by same authorPrevious page (June 2009, week 4)Back to main SAS-L pageJoin or leave SAS-L (or change settings)ReplyPost a new messageSearchProportional fontNon-proportional font
Date:         Mon, 29 Jun 2009 00:44:24 +0000
Reply-To:     toby dunn <tobydunn@HOTMAIL.COM>
Sender:       "SAS(r) Discussion" <SAS-L@LISTSERV.UGA.EDU>
From:         toby dunn <tobydunn@HOTMAIL.COM>
Subject:      Re: A better more flexible macro interface that minimizes global
              macro variables
Comments: To: xlr82sas@aol.com
In-Reply-To:  <ed91cf2b-c6f7-4ee9-b2c7-926c5ee74e82@j3g2000yqa.googlegroups.com>
Content-Type: text/plain; charset="Windows-1252"

I can tak eissue with this as SAS makes it very easy with a couple of exceptions to know the scope of a macro variable and to decide the scope of a macro variable, however it does take knowing what one is doing with the macro facility, then again that should be a pre requisite before writing any code in any language. I do agree that glbal macros can be dangerous but then again most code can be dangerous in the hands of someone who doesnt know enough to know he does know enough.

Toby Dunn

"Don't bail. The best gold is at the bottom of barrels of crap." Randy Pausch "Be prepared. Luck is where preparation meets opportunity." Randy Pausch

> Date: Sun, 28 Jun 2009 16:37:40 -0700 > From: xlr82sas@AOL.COM > Subject: A better more flexible macro interface that minimizes global macro variables > To: SAS-L@LISTSERV.UGA.EDU > > Hi SAS-Lrs, > > For a better view of this long post(without all the wrapping), go to > http://homepage.mac.com/magdelina/.Public/utl.html and click on > bettermacro. > > %let > pgm=better_macro; > /* The purpose of this post is to discuss 'better' macro > interfaces > */ > /* SAS does not make it easy to scope variables. In paticular it does > not */ > /* allow us to send results back to the calling macro unless we make > the results global*/ > /* creating global macro variables is usually not a good idea (except > for functions) */ > > /* we would like to do the following > */ > /* but not have Area and Volume macro variables global > */ > /* Code below passes all the special cases 'proc sql' 'go to' > 'sysbuff' at invocation > */ > /* THIS IS NOT EASY > */ > > %macro GetAreaVolume > (figure=Square); > %AreaVolume > (figure=square,length=3,Area=,Volume=); > %put Area=&Area > Volume=Volume; > /*we would like Area=9 Volume=27 > */ > %mend > GetAreaVolume; > > %GetAreaVolume; > %put Area=&Area > Volume=Volume; > /* Area and Volume are not global > */ > Area=&Area > Volume=&Volume > > /* HERE IS THE 1st SOLUTION > */ > > %symdel > length; > %symdel > area; > %symdel > volume; > %macro AreaVolume > ( > > Figure=square > ,Length=3 > /* the local macro variables BELOW are returned to the calling > proram */ > /* these macro variables will be placed in the scope of the > calling program > */ > /* supplying values for these macro variables on invocation will > have no effect */ > /* the argument list is the contract between the calling and > called program > */ > ,Area=Area_Square > ,Volume=Volume_Square > ); > /* I put some dummy code her > */ > proc sql;select * from > sashelp.class;quit; > data x;set > sashelp.class;run; > %goto > foo; > %foo : %let x = this is a > test; > /* do not make any changes ater this comment > */ > Data > _null_; > Area =put(&length**2,4. - > l); > Volume=put(&length**3,4. - > l); > call symputx > ('Area',Area); > call symputx > ('Volume',Volume); > run /* do not put a semicolon after run > */ > %mend > AreaVolume; > > %macro GetAreaVolume > (Type=GeometicFigures); > %AreaVolume(Figure=square,Length=3,Area=,Volume=); /* this > semicolon is required > */ > %put &Area &Volume; /* area and volume are > returned data > */ > %mend > GetAreaVolume; > > %GetAreaVolume; > %put &Area > &Volume; > > /* HERE IS THE 2nd > SOLUTION > */ > /* It is a slight variation on 6th solution below (Thnaks to Richard > A. DeVenezia) > */ > /* This uses an agreed upon save area in the calling > program > */ > /* This save area should be identical for all calling > macros > */ > /* %local Utl_ReturnCount Utl_ReturnNames > Utl_ReturnValues > */ > /* Utl_ReturnCount = Number of macro variables returned from called > macro. */ > /* Utl_ReturnNames = Names of macro variables > returned > */ > /* Utl_ReturnValues = Values assigned to macro variables > returned > */ > > %symdel > length; > %symdel > area; > %symdel > volume; > > %macro AreaVolume > ( > > Figure=square > ,Length=3 > /* do not specify these values on macro call. Little c means > contents > */ > /* this shows the contract between called and calling macro > and is valuable information */ > ,ReturnCount =c(2) /* number of > macro variables returned */ > ,ReturnNames =c(Area Volume) /* name of > macro variables returned */ > ,ReturnValues=c('%let area=9;%let volume=27;') /* values for > macro variables */ > ); > > %let > &ReturnCount=2; > %let &ReturnNames=Area > Volume; > > Data > _null_; > Area = cats('%let Area =',put > (&length**2,4.),';'); > Volume = cats('%let Volume=',put > (&length**3,4.),';'); > Call symputx("&ReturnValues",cats > ("'",Area,Volume,"'")); > > run; > > %mend > AreaVolume; > > %macro GetAreaVolume > (Figure=square); > /* This is the save area. That will be populated from the called > program */ > /* All parent macros that expect return > information > */ > /* from the called macro must have this exact local > statement > */ > %local Utl_ReturnCount Utl_ReturnNames > Utl_ReturnValues; > > %AreaVolume > ( > > Figure=square > ,Length=3 > ,ReturnCount =Utl_ReturnCount /* number of macro variables > returned */ > ,ReturnNames =Utl_ReturnNames /* name of macro variables > returned */ > ,ReturnValues=Utl_ReturnValues) /* values for macro > variables > */ > > %put > &Utl_ReturnValues; > %sysfunc(dequote > (&Utl_ReturnValues)) > %put > Utl_ReturnCount=&Utl_ReturnCount; > %put > Utl_ReturnNames=&Utl_ReturnNames; > %put Area=&Area > Volume=&Volume; > %mend > GetAreaVolume; > > %GetAreaVolume > (Figure=square); > %put Area=&Area > Volume=&Volume; > > > /* HERE IS THE 3rd SOLUTION > */ > /* IT USES THE SAS AUTOMATIC GLOBAL MACRO VARIABLE AFSTR1 > */ > %symdel > length; > %symdel > area; > %symdel > volume; > > %macro quit/des="get return data from child macro - eliminates global > macro variables"; > sysfunc(dequote(&&afstr1)) = > dummy > %mend > quit; > > %macro AreaVolume > ( > > Length=3 > ,Area=Area_Square > ,Volume=Volume_Square > ); > %let > afstr1=; > /* insert additional code her > */ > > /* do not make any changes ater this comment > */ > Data > _null_; > Area = cats('%let Area =',put > (&length**2,4.),';'); > Volume = cats('%let Volume=',put > (&length**3,4.),';'); > Call symputx('afstr1',cats > ("'",Area,Volume,"'")); > > run; > > %mend > AreaVolume; > > %macro GetAreaVolume > (Figure=Square); > %AreaVolume > (Length=3,Area=,Volume=); > %let noop=%eval(% > %quit); > %put &Area > &Volume; > %mend > GetAreaVolume; > > %GetAreaVolume; > /* the macro variables are not global !!!!! > */ > %put &Area > &Volume; > > /* HERE IS THE 4th > SOLUTION > */ > /* Declare the macro variables Area and Volume global using > symputx > */ > /* unfortunately we have to remove the variables from the macro > arguments > */ > /* this leaves the contract between macros less > clear > */ > %symdel > length; > %symdel > area; > %symdel > volume; > %macro AreaVolume > ( > > Length=3 > ); > /* insert additional code her > */ > Data > _null_; > Area =put(&length**2,4. - > l); > Volume=put(&length**3,4. - > l); > call symputx > ('Area',Area,'G'); > call symputx > ('Volume',Volume,'G'); > > run; > %mend > AreaVolume; > > %macro GetAreaVolume > (Figure=Square); > %global Area > Volume; > %AreaVolume > (Length=3); > %put &Area > &Volume; > %mend > GetAreaVolume; > > %GetAreaVolume; > /* the macro variables are not global !!!!! > */ > %put &Area > &Volume; > > /* HERE IS THE 5th SOLUTION > */ > /* same as second solution but does not require the quit macro > */ > /* replaces the quit macro with %sysfunc(dequote(&afstr1)); > */ > /* This method uses and intermediate macro variables and allows > */ > /* for arrays of macro variables to be retuned to the calling > */ > /* program > */ > > %macro AreaVolume > ( > > Length=3 > ,Area=Area_Square > ,Volume=Volume_Square > ); > %let > afstr1=; > /* insert additional code her > */ > > /* do not make any changes ater this comment > */ > Data > _null_; > Area = cats('%let Area =',put > (&length**2,4.),';'); > Volume = cats('%let Volume=',put > (&length**3,4.),';'); > put area= > volume=; > Call symputx('afstr1',cats > ("'",Area,Volume,"'")); > > run; > > %mend > AreaVolume; > > %macro GetAreaVolume > (Figure=square); > %local Area > Volume; > %AreaVolume > (Length=3,Area=,Volume=); > %sysfunc(dequote > (&afstr1)); > %put &Area > &Volume; > %mend > GetAreaVolume; > > %GetAreaVolume; > /* the macro variables are not global !!!!! > */ > %put &Area > &Volume; > > > /* HERE IS THE 6th > SOLUTION > */ > /* classic global macro > approach > */ > /* Declare the macro variables Area and Volume global using > %global > */ > /* unfortunately we have to remove the variables from the macro > arguments > */ > %symdel > length; > %symdel > area; > %symdel > volume; > %global area > volume; > %macro AreaVolume > ( > > Length=3 > ); > /* insert additional code her > */ > Data > _null_; > Area =put(&length**2,4. - > l); > Volume=put(&length**3,4. - > l); > call symputx > ('Area',Area); > call symputx > ('Volume',Volume); > > run; > %mend > AreaVolume; > > %macro GetAreaVolume > (Figure=Square); > %global Area > Volume; > %AreaVolume > (Length=3); > %put &Area > &Volume; > %mend > GetAreaVolume; > > %GetAreaVolume; > /* the macro variables are not global !!!!! > */ > %put &Area > &Volume; > > /* HERE IS THE 7th > SOLUTION > */ > /* Courtesy of Richard A. DeVenezia > */ > /* Note it is possible to use this method with pure macro code > */ > /* this method uses macro variables that contain macro variable names > */ > %symdel > length; > %symdel > area; > %symdel > volume; > %macro AreaVolume > ( > > Figure=square > ,Length=3 > ,ReturnVars= > ,ReturnArray= > ); > %let &ReturnVars=Area > Volume; > Data > _null_; > Area = cats('%let Area =',put > (&length**2,4.),';'); > Volume = cats('%let Volume=',put > (&length**3,4.),';'); > Call symputx("&ReturnArray",cats > ("'",Area,Volume,"'")); > > run; > %mend > AreaVolume; > > %macro > GetAreaVolume; > %local Names > Values; > %AreaVolume > (Figure=square,Length=3,ReturnVars=Names,ReturnArray=Values) > %sysfunc(dequote > (&Values)) > %put > Names=&Names; > %put Area=&Area > Volume=&Volume; > %mend > GetAreaVolume; > > %GetAreaVolume; > %put Area=&Area > Volume=&Volume; > > /* HERE IS THE 8th SOLUTION > */ > /* Pure macro code solution > */ > /* Very similar to 6th method > */ > /* See earlier SAS post > */ > %macro inner(mvstatements=, > mvlocals=); > %put > statements=&statements; > %let &mvlocals = status_1 status_2 > status_3; > %let &mvstatements = %nrquote(&&&mvstatements)%nrstr(%let ) > status_1=good1%str > (;); > %let &mvstatements = %nrquote(&&&mvstatements)%nrstr(%let ) > status_2=bad1%str > (;); > %let &mvstatements = %nrquote(&&&mvstatements)%nrstr(%let ) > status_3=ugly1%str > (;); > %mend; > > %macro > outer; > %local locals > statements; > %inner (mvstatements=statements, > mvlocals=locals) > %put > statements=&statements; > %unquote > (&statements) > %put > status_1=&status_1; > %put > status_2=&status_2; > %put > status_3=&status_3; > %mend; > %outer; > %put > status_1=&status_1; > > / > * > There is one drawback with passing macro variable names to inner > macros, > collisions. > > When the macro variable named in the parameter matches a macro > variable > local to the inner scope, the macro var you expect to have been set by > the > inner macro will remain unchanged upon return from the macro call. > This is > because the assignment was done to a macro var local to an inner > scope > that no longer > exists. > */ > > %macro inner > (mvstatus=); > %local > status; > %let status = > FAILURE; > %let &mvstatus = > SUCCESS; > %put inner: > &mvstatus=&&&mvstatus; > %mend; > > %macro > outer; > %local > status; > %let > status=UNKNOWN; > %inner > (mvstatus=status) > %put outer: status=&status (expecting > SUCCESS); > %mend; > > %outer > / > * > outer: status=UNKNOWN (expecting > SUCCESS) > */ > > /* was surprised that the call symput method worked > */ > /* I not sure why > */ > %symdel > status; > %symdel > mvstatus; > > %macro inner > (mvstatus=); > %local > status; > %let > status=FAILURE; > data > _null_; > call symputx > ("&mvstatus",'SUCCESS'); > > run > %mend > inner; > > %macro > outer; > %local > status; > %let > status=UNKNOWN; > %inner > (mvstatus=status); > %put outer: status=&status (expecting > SUCCESS); > %mend; > > %outer > %put &status;

_________________________________________________________________ Windows Live™ SkyDrive™: Get 25 GB of free online storage. http://windowslive.com/online/skydrive?ocid=TXT_TAGLM_WL_SD_25GB_062009


Back to: Top of message | Previous page | Main SAS-L page