Date: Tue, 17 Dec 2002 11:53:17 -0500
Reply-To: Ian Whitlock <WHITLOI1@WESTAT.COM>
Sender: "SAS(r) Discussion" <SAS-L@LISTSERV.UGA.EDU>
From: Ian Whitlock <WHITLOI1@WESTAT.COM>
Subject: Re: macro design
Content-Type: text/plain; charset="iso-8859-1"
Ron,
You are absolutely correct about where the semi-colon problem lies and raise
good questions about design.
You changed my macro from
%MACRO BigIf(cond=x=1,consequent=put x=);
if &cond then &consequent ;
%MEND;
to yours saying
> Personally I would write such as a function:
> %MACRO BigIf(cond=x=1,consequent=put x=);
> &cond then &consequent
> %MEND;
From the problem point of view, I point out that in the header my macro
explicitly stated
purpose:
generate a SAS IF statement.
You changed the requirements of the macro into making a partial statement
and consequently the problem. In general, changing the problem is often the
best solution, but not in my puzzles.
From a design point of view I have to ask you why you would write a macro
that simply separates two arguments by the four letters THEN? Oops, perhaps
you could ask almost the same question of me. However, my position is
different. I wanted a simple example and hoped that you would fill in with
thoughts of the more complex problem that I had in mind. So my answer is
that I would not write such a macro at all, but that it represents a real
problem.
In this trivial situation you removed the IF and the semi-colon from the
macro turning the code into what I would call a SAS partial statement macro.
In doing so, the calling code became clearer because the context is left to
the user's IF rather than just the macro name while incidentally turning a
null statement into something useful. Now the question is: In the real
problem, the one I had in mind, is such a technique a good one? I agree it
was a bit unfair of me to not be more explicit about my thoughts in the
first place. You get an A for the first test. So let's try again, although
this time I will have to drop your template for more serious work. Consider
a somewhat more realistic need for a similar macro on the fictitious madman
project.
%macro BigIf ( testroot = x ,
result = y ,
testvalue = 5 ,
mult = 3,
max = 7
) ;
%* make the complex IF statement
required throughout the code for the madman project
;
%local i j
cond /* the condition for the big if statement */
vars /* variable list used in the consequent */
;
%let cond = &testroot&mult = &testvalue ;
%let vars = &testroot&mult ;
%do i = 2 %to &max ;
%let j = %eval ( &mult * &i ) ;
%let cond = &cond or &testroot&j = &testvalue ;
%let vars = &vars &testroot&j ;
%end ;
if &cond then
do ;
chkflag = 1 ;
&result = sum (of &vars ) ;
end ;
%mend BigIf ;
Here is the simple test code.
data _null_ ;
retain a1-a50 10 ;
%bigif ( testroot = a ,
result = b ,
testvalue = 10 ,
mult = 4,
max = 10
)
put b= ;
run ;
Now from a design point of view, I would consider it down right wrong to put
the IF in the consuming code. Would you still suggest that the semi-colon
on the final END statement be removed in order to feed your habit of adding
null statements after macro calls? If so, I hope you are saying to
yourself, "write reminder to add comment that the END statement must not be
completed".
Now I suggest that any time one has to add comments like that, it is a
indication of a big violation in some principle.
In this macro, I think, that there is no reasonable way to interpret the
macro as generating a partial SAS statement, consequently there is no
reasonable way to introduce partial SAS statements without creating a design
error; hence the need for a comment when removing the semi-colon.
I apologize for the complexity, but that is what I was trying to avoid when
I first posed the problem.
Incidentally, I originally wrote the macro starting with IF. It is
interesting to contrast the two styles of macro coding. I quickly erased
the code embarrassed that I might even consider showing it in public.
However I had already tested it, so it was still just a few recalls away.
Here it is, JUST FOR COMPARISON, I know better than to write code like this.
%macro BigIf ( testroot = x ,
result = y ,
testvalue = 5 ,
mult = 3,
max = 7
) ;
%* make the complex IF statement
required throughout the code for the madman project
;
%local i j ;
if &testroot&mult = &testvalue
%do i = 2 %to &max ;
%let j = %eval ( &mult * &i ) ;
or &testroot&j = &testvalue
%end ;
then
do ;
chkflag = 1 ;
&result = sum (of %do i = 1 %to &max ;
%let j = %eval ( &mult * &i ) ;
&testroot&j
%end ;
) ;
end ;
%mend BigIf ;
IanWhitlock@westat.com
-----Original Message-----
From: Fehd, Ronald J. (PHPPO) [mailto:rjf2@CDC.GOV]
Sent: Monday, December 16, 2002 3:51 PM
To: SAS-L@LISTSERV.UGA.EDU
Subject: macro design
After spending several hours over the weekend
talking to myself about this problem
I casually present the incomplete transcript.
Subject: A.Word.A.Day--frangible
frangible (FRAN-juh-buhl) adjective
Readily broken; breakable.
...
This week's theme: kangaroo words,
words that have a joey (a smaller word with a similar sense) within them.
The word "frangible" has three generations of kangaroos:
its joey "fragile" which in turn has its own little one "frail".
You can never solve a problem on the level on which it was created.
-Albert Einstein, physicist, Nobel laureate (1879-1955)
while reading Kopka & Daly's A Guide to LaTeX
I come across the notation that some commands are fragile
while others are robust.
Ian Whitlock <WHITLOI1@WESTAT.COM> writes in
Subject: tip macro function CLOCK V2 debugging
> I wrote the following code.
> Please do not criticize the inanity of the code.
> It is not meant to be good principled code.
> It is meant to demonstrate a point.
%MACRO BigIf(cond=x=1,consequent=put x=);
if &cond then &consequent ;
%MEND;
which I would like to continue the discussion
on whether a preposition or a semicolon is a good thing
to end a sentence or a macro with.
/*test data
data w ;
do x = 1 to 3 ;
output ;
end ;
run ;
data sub ;
set w ;
%BigIf(cond=x=1,consequent=output sub);
run ;
/*test data closure*/
options mprint ;
data sub ;
set w ;
%BigIf(cond=x=1,consequent=output sub);
else
%BigIf(cond=x=3,consequent=put x=);
run ;
Ian, I wonder if this is the original example
that you use to illustrate why
you do not recommend ending macro calls with a semicolon?
At any rate I recommend ending macro calls with semicolons
because the majority of my experience is writing macro procedures
which contain one or more steps.
I've had to change my thinking in the last week,
due to your helpful critique, for which I am grateful.
I do not have much experience writing intra-date-step macros
which are saved outside the program.
Your above example is in that gray area between function and procedure.
Admittedly very close to the function end of the scale.
My question for discussion is what is correct or appropriate
to keep in mind when asked for such code?
Personally I would write such as a function:
%MACRO BigIf(cond=x=1,consequent=put x=);
&cond then &consequent
%MEND;
then to be used as:
data sub ;
set w ;
if %BigIf(cond=x=1,consequent=output sub);
else
if %BigIf(cond=x=3,consequent=put x=);
run;
This gets around the gotcha of having a complete
if statement in the macro
and presents the user with a
<macro call that looks like a SAS statement>.
I am aware of the issues behind the obvious misuse here:
IF statements can be unary, and they can be independent.
The problem with ELSE is the IF statement becomes part
of an ELSE-delimited list of IF statements
i.e.: XOR(IF-1,IF-2,IF-3,...)
and any ELSE IF-i must come immediately after the closure of IF-(i-1)
For those of you that are still reading,
Ian's point was that using my style sheet,
the code would generate a null statement <;> between IF-1 and IF-2
e.g.
if <cond-1> then <consequent-1>;
;%*side effect of my style sheet;
else
if <cond-2> then <consequent-2>;
I wonder whether there's any way to keep idiots from asking us
to write code that's fragile?
Ron Fehd the macro maven CDC Atlanta GA USA RJF2@cdc.gov
My computer must be broken:
whenever I ask a wrong question,
I get a wrong answer.
-- Pot-Shots by Ashleigh Brilliant
Programming today is a race between software engineers
striving to build bigger and better idiot-proof programs,
and the Universe trying to produce bigger and better idiots.
So far, the Universe is winning.
- Rich Cook