Date: Sat, 27 Jul 2002 08:25:51 -0700
Reply-To: Roger DeAngelis <xlr82sas@AOL.COM>
Sender: "SAS(r) Discussion" <SAS-L@LISTSERV.UGA.EDU>
From: Roger DeAngelis <xlr82sas@AOL.COM>
Organization: http://groups.google.com/
Subject: Re: Labeling columns names through macros
Content-Type: text/plain; charset=ISO-8859-1
Quentin,
I think you comment about "open code 'global' macro variables" is
correct, I avoid them at all costs. It is very difficult to debug an
interactive program,
if that program acts differently the second time you run it.
Run the following program twice in display manager ( trivial example
of
a sinister often hard to find problem).
Consider
%macro outglob;
%put glb_var=&glb_var;
%mend outglob;
%outglog;
run;
%let gbl_var=Global_Var;
run;
I almost always wrap my code in a macro, even if I intend
to remove the macro after debugging. Flame away please.
Debugging is more difficult, but SAS has a rich set of
options, I recently added spool to utlopts. My trivial
macros utlopts and utlnopts are very useful, especially
utlnopts. Utlnopts turns everything off so only severe
errors and no log(except %put/put) and list output result.
There are times when
I want to see just mprint or symbolgen or macrogen or whatever.
It is also interesting that many languages prefer
tighter boundaries on code, I think IML now issues a warning
if you do not start your code with at least a 'MAIN' scope.
Personally I like the option of opencode or macro code.
I try yo use global variables for command line functions only.
The folowing two line of code turns on just the option you want
%Utlnopts;options mprint;run;
Incidentally I think an easy solution to your problem might be
along the lines of:
%macro MAIN;
proc sql;
create table control
(
var char(1),
label char(12)
);
insert into control
values("A","Label for A")
values("B","Label for B")
values("C","Label for C");
quit;
run;
%macro ShowLabel(var=,control=control);
proc sql noprint;
select var || ':' || Label
into :VarAndLabel
from &control
where var="&var";
quit;
run;
data _null_;
put "&VarAndLabel";
run;
%Mend showlabel;
data _null_;
set control;
CALL EXECUTE('%ShowLabel(var='||var||')');
RUN;
run;
%mend MAIN;
%utlnopts;
%MAIN;
QuentinMcMullen@WESTAT.COM (Quentin McMullen) wrote in message news:<15104802790BD411A2C100D0B73EA33C02CA37E5@remailnt3-re01.westat.com>...
> Ian Whitlock wrote in part:
>
> > When a macro is invoked by CALL EXECUTE, it executes during the execution
> of
> > a DATA step; hence the rules of macro processing must change, since steps
> > cannot be executed in steps. The code is generated (not compiled or
> > executed) step by step and dumped to the input stack for later execution.
> > This means that the variable, DescriptionList, will not be created until
> the
> > SQL code runs. However, the reference in the subsequent steps has already
> > been generated. You have a problem.
>
> It has taken me a while to understand everything said above. Thought I
> might throw out a few more thoughts and an example and see if folks will
> correct me where I'm off.
>
> Here is a smaller example using SQL in a macro called by excute, based on
> Benjamen's code:
>
> data control;
> INPUT Var $1 Label $12.;
> list;
> CARDS;
> A Label for A
> B Label for B
> C Label for C
> ;
> run;
>
> %macro ShowLabel(var=,control=control);
> proc sql noprint;
> select var || ':' || Label
> into :VarAndLabel
> from &control
> where var="&var";
> quit;
> data _null_;
> put "&VarAndLabel";
> run;
> %mend ShowLabel;
>
> data _null_;
> set control;
> CALL EXECUTE('%ShowLabel(var='||var||')');
> RUN;
>
> During testing, I quickly became confused by results similar to Benjamen's,
> i.e. always having &VaraAndLabel resolve to the last value. Eventually, I
> started thinking about macro variable scope.
>
> If you batch submit the above code, it will "work". You will get a bunch of
> warnings about unresolved macro variable &VarAndLabel (these are produced
> because the macro executes during the data step, and tries to resolve
> &VarAndLabel, before the SQL code has run). But the unresolved &VarAndLabel
> is passed to the input stack, and by the time it reaches the top,
> &VarAndLabel has been created, and the code "works".
>
> But if you are running interactively, and run it again, the value for the
> put statement will always be from the last record, "C: Label for C". And
> you get no warnings.
>
> So what is going on? It's that old enemy, the global macro variable!
> &VarAndLabel is GLOBAL, so on later calls to the macro, &VarAndLabel can
> resolve when the call execute runs (i.e. before the SQL has executed)!
>
> So at first I thought "This is horrible! I've got my SQL statement inside
> of a macro, it should be creating local macro variables!!". If I didn't use
> call execute, and just coded, %ShowLabel(Var=A), &VarAndLabel would be
> appropriately local. Why should call execute change this? And why does
> adding a %local statement make it worse?
>
> And then it hit me. Yes the SQL step is "inside" of my macro. But when I
> call the macro with call execute, it simply generates all of the code and
> dumps it on the input stack. When the SQL step (or same for a data step
> using call symput) executes, it's actually running in OPEN code. There is
> no macro executing, and there is no local table to write to. So the only
> option is take write to the global table.
>
> Is that right?
>
> I guess the real issue here is when does a macro execute? I often
> mistakenly think of call execute generating a call to the macro, which then
> executes after the data step. But that is wrong. Call execute will execute
> the macro during the step, and send the generated code to the input stack.
> Ian suggested:
> CALL EXECUTE('%nrstr(%Add_Label(MType=)'||MType||',Master_DB=ID_DB)');
>
> as a way to keep the macro from executing during the data step execution,
> and instead have call execute write the macro call to the input stack. I'm
> wondering if this would be good practice in general?? To my mind, it keeps
> things simpler. Call execute simply generates the macro call. Then the
> macro is executed outside of the data step. It seems to make the log more
> clear as well, because you only get one call execute line for each macro
> call, followed by the mprint code for that macro.
>
> Thoughts/feedback much appreciated,
> --Quentin
|