LISTSERV at the University of Georgia
Menubar Imagemap
Home Browse Manage Request Manuals Register
Previous messageNext messagePrevious in topicNext in topicPrevious by same authorNext by same authorPrevious page (December 2005, week 2)Back to main SAS-L pageJoin or leave SAS-L (or change settings)ReplyPost a new messageSearchProportional fontNon-proportional font
Date:         Sat, 10 Dec 2005 19:50:24 +0000
Reply-To:     iw1junk@COMCAST.NET
Sender:       "SAS(r) Discussion" <SAS-L@LISTSERV.UGA.EDU>
From:         Ian Whitlock <iw1junk@COMCAST.NET>
Subject:      Re: MAcro Design was (Re: Macro quoting essentials)
Comments: cc: lzhang9830@YAHOO.COM, toby dunn <tobydunn@HOTMAIL.COM>

Subject: Re: MAcro Design was (Re: Macro quoting essentials) Summary: Discussion of design issues in context of help suggestion and the macro PRINTZ Respondent: Ian Whitlock iw1junk@comcast.net Lei, You have an interesting idea to add the PARMBUFF option to all macro calls so that a call such as %printz; or %printz() will produce help. For the uninitiated, I note that adding the PARMBUFF option will preserve positional and keyword parameters. For example, %macro q ( x, y , z=stuff ) / parmbuff; %put x=&x y=&y z=&z :&syspbuff ; %mend q ; %q(a,b) produces x=a y=b z=stuff :(a,b) Design issues fall into two categories - system issues across macros and issues within a macro. Your help suggestion is a system issue. It certainly makes sense to have a unified approach to help, but there are several possibilities. 1) Empty argument list Disadvantages: User cannot call with all defaults Ugly %PUT statements Macro must use PARMBUFF option One level of help Closed to very complex macros (e.g. macro with 20 interacting parameters) Advantages: Immediate interactive or batch use Intuitive for user used to UNIX commands 2) HELP parm (values ?,yes,y,1...) Disadvantages: User must remember and type the key word HELP= Ugly %PUT statements One level of help Closed to very complex macros (e.g. macro with 20 interacting parameters) Advantages: Immediate interactive or batch use Intuitive once learns to type "HELP=?" 3) External documentation: Disadvantages: User must learn to use the help facility Complex to set up Long macros if documentation stored with macro Advantages: Open to multiple levels of help Immediate interactive or batch use when controlled by macro or interactive when some help facility is used Open to very complex macros particularly when an external help system is used In the long run I think #3 has to win because it is open ended. For simplicity and keeping it within SAS, I liked a system where we structured the macros. 1) Single line comment before macro stating general purpose. 2) One parameter per line with short clarifying comment. 3) Closing MACRO statement with ";" on a separate line. 4) Immediately followed by PL1 comment block with closing */ on a separate line. The user called a macro %DOC and could specify LIBRARY (with default company autolib), MACRO (member in library), level of documentation (short - one line comment plus %MACRO statement, or long - added initial comment block. If I had been writing for UNIX users I might have called the macro MAN instead of DOC. Now let's turn to your specific example of %PRINTZ. The only advantages I see to your %macro printz(x)/parmbuff; are 1) It meets your help philosophy. 2) It allows the user to write a comma separated list without hiding the commas from the macro facility. It is not clear to me why a comma separate list should be important. SAS is generally not a comma separated language with the exceptions of function and macro calls, SQL. Perhaps it is a question of view, but I see the list as one argument, and particularly do not want to hide this fact by making appear as a set of arguments. Something can be said for simplicity, but I think the main design goals should be 1) functionality 2) expandability 3) global readability - how does the macro accomplish it purpose 4) local readability - what does this line mean? 5) usability I tend to think the order given is the order of priority, but in fact these are always competing goals and in any particular case one should be willing to give up something from one goal to obtain something considered more important in another because of the particular situation. In a one positional parameter macro you don't have to remember anything except what kind of a parameter value is expected. But what happens when you want to expand the functionality of the macro? For example, it would be desirable to have an OBS limit for PRINTZ. Do you want to lock the user into requiring OBS come first or last? Since I see macro development as a growth of functionality which usually requires adding parameters for more detailed specification, I would want OBS to be a keyword parameter. That is largely why I see positional parameters as play. Consider %Q(A,B,C,D) Is that a phony list using PARMBUFF? Is B the output data set name or input ID variable? Is D a keyword for deleting a certain type of record or something else? I consider it neither easy to remember the order nor to read this code once I have looked up what goes where. Consequently, I see positional parameters as buying simplicity and locking you into stunted growth for the life of the macro. I cannot say proc print w ; why should I not have to name the data parameter in macro call? Well we could decide that all subsequent parameters for %PRINTZ must be keyword parameters. So what is the consequence? Now we have to identify the end of the list by anything containing an =-sign. But I would like the ability to drop or keep variables for a particular data set and W(DROP= X Y) will cause problems. This causes problems with the way we decided to end the list, and the use of space as a separator, and the use of parentheses as separators. In fact if we plan on having any significant growth for this macro we need parameters, SEP= to provide the list separator, and ENDER= to provide the list ender. The problem was that we tried to have a list without adequate machinery to handle a list in the face of complexity. Here is my suggestion. /* print list of data sets */ %macro printz( x /* list of data sets */ , obs=20 /* obs limit null is max */ , sep=%str(, ) /* list of list separators */ , ender== /* end list indicator */ ) /parmbuff ; /* printz - print list of data sets parameters: x - positional required unless help requested name and type inherited from Lei obs - default 20, null means no limit sep - default space and comma inherited from Lei ender = default = inherited when OBS added usage: %printz(lib.w1 w2) - print two data sets 20 obs %printz(lib.w(keep=id obs =5)/w/#/,sep=/,ender=#) note need for final separator after ender */ %local num dsname ; %if %length(&syspbuff)= 0 or %length(&syspbuff)=2 %then %do; /* Print help */ %put; %put Help on Macro %nrstr(%%Printz):; %put ...; %put End of Help; %end; %else %do; /* Main */ %let num=1; %let syspbuff = %substr(%superq(syspbuff),2,%length(&syspbuff)-2) ; %let dsname=%scan(%superq(syspbuff),&num, &sep); %do %while(%length(&dsname) and not(%index(&dsname,&ender))); %if %length(&obs) > 0 and %index(&dsname, %str(%()) = 0 %then %let dsname = &dsname(obs=&obs) ; proc print data=&dsname width=minimum; run; %let num=%eval(&num+1); %let dsname=%scan(%superq(syspbuff),&num, &sep); %end; %end; %mend; data w1 w2 w3 ; retain x y 3 ; do obs = 1 to 50 ; output ; end ; run ; %printz() %printz( w1 , work.w2 ) %printz(w1, w2 ,obs=2) %printz(w1, work.w3(keep=x obs=4),*,sep=%str(,),ender=*,obs=) %printz(work.w1/w2/w3/#/,obs=4,sep=/,ender=#) Quoting quiz: Lei did not use quoting on &SYSPBUFF when scanning it, while my version required it. Why? What obstacle removal caused the need for this quoting? In conclusion, I have managed to add my desired features without losing the macros original philosophy. However, I think it would have been easier and in the long run better to have simply started with the list concept and admited that lists need a separator and ender. Moreover, for flexability, those elements should be in the hands of the user. Is this version of the macro more difficult to use? Well, yes, if you need its features, but, no, if don't. On the other hand, part of that difficulty in handling new features comes from the need to unlearn the original simplicity. So maybe simplicity shouldn't be desired for complex task requirements. Hopefully this exercise has helped someone to see how early design decisions may lead one to build machinery that cannot last over a long period of time and active growth. In general, be very careful to get real value for simplicty when limiting usage by stunting growth. Saving a few keystrokes or looking familiar when it isn't, doesn't qualify. Ian Whitlock ================ Date: Fri, 9 Dec 2005 13:20:36 -0800 Reply-To: lzhang9830@YAHOO.COM Sender: "SAS(r) Discussion" From: Lei Zhang <lzhang9830@YAHOO.COM> Organization: http://groups.google.com Subject: Re: MAcro Design was (Re: Macro quoting essentials) Comments: To: sas-l In-Reply-To: <BAY101-F10DEF612E041FFF7F7E28ADE450@phx.gbl> Content-Type: text/plain; charset="iso-8859-1" Hi Toby, Attached is the example macro with online help functionality that I purpose. The macro is modified from your %printz by adding pbuff option. To get onlne help, a macro user can simply type the macro name and run it. He/she don't have to remenber or type any other extra parameters for help. For example, type %Printz; the macro show the usage as follows: Help on Macro %Printz: ... End of Help A user can also print one or more datasets with its normal parameters: %Printz(sashelp.class sashelp.vstable); I will appreciate if you have other approach without using vararg feature. Thanks.

The source code is listed as follows for your quick review: %macro printz(x)/parmbuff; %if %length(&syspbuff)= 0 or %length(&syspbuff)=2 %then %do; /* Print help */ %put; %put Help on Macro %nrstr(%%Printz):; %put ...; %put End of Help; %end; %else %do; /* Main */ %let num=1; %let dsname=%scan(&syspbuff,&num, %str(%(%), )); %do %while(%length(&dsname)); proc print data=&dsname width=minimum; run; %let num=%eval(&num+1); %let dsname=%scan(&syspbuff,&num, %str(%(%), )); %end; %end; %mend;


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