Date: Mon, 23 Dec 2002 18:22:02 -0800
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: return value from macro?
Content-Type: text/plain; charset=ISO-8859-1
Good points Ian,
SoapBox on ( all the following may not be true )
Construtive critical comments are welcome, (Thanks Ians)
If someone told me I had to choose two of the following
SAS components.
1. Data step
2. SCL/AF
3. SQL
4. IML
5. Macro
6. SAS-C
I would choose the macro and the data step languages.
If I could update and create SAS datasets with the macro language,
and had to pick one language, it would be the macro language.
I would then use the macro language to create the datastep language.
It would be slow, but with future 50ghz processors possible, who
cares.
===========================================================================
Peek and Poke are two of the seven magic instructions?, and are
fundamentally very powerful. Peek and Poke are the
load and store in assembly language.
1. Commuication between languages?
2. Communication within a language
3. Fast 'Binary large object processing' BLOBS.
Move this 1meg binary string from place to place(rb8.).
Thanks Paul.
4. To determine how SAS really stores data
5. Quick pointers BLOBS.
The techniques, in the previous post should not be implemeted
by novice users and should be heavily documented.
I would not use the peek and poke method in production.
I might explore its use in a single one time adhoc report.
Users may want to experiment with these techniques and perhaps
inflence SAS to better support peek and poke. ( peek_check,
poke_check and valid return area etc??).
Until SAS sets aside some memory locations for a return area,
users probably should not use peek and poke for macro return
arguments( between language communication).
In IBM 390 assembly language(from memory??), register 14 was
set aside for the address of a 'return' area of memory. This return
area had
a standard structure, first word had the length of
the memory area, following words had #args and length of args(I
think).
Memory protection exceptions are more common in assembly language than
SAS, since by nature assemblers can put anything anywhere.
But a memory protection error is not the end of the world.
It would be nice if SAS could set aside some memory addresses
for SAS users, SAS or the opsys would avoid these addresses.
Lacking this support, it might be nice if the SAS community could
agree on a return area?
Macro processors in the pure sense, just generate text, usually
programming statements. Dietel, in 'Intro to Op Sys - Unix, Vax, CP/M,
MVS and
VM' defines a macro as a 'task which embodies several language
instructions' and macro expansion as the generation of the original
instructions.
In 1979(from memory?), a SAS macro had the form:
MACRO PRNTIT
Proc Print data=Hardcode;
Id year;
Var income;
Title MY INCOME;
%
No arguments, no logic and no arithmetic capapability.
This is a pure text generator.
Today the macro language is a 'general purpose language?', because
it can perform the magic seven instructions?.
SoapBox off;
WHITLOI1@WESTAT.COM (Ian Whitlock) wrote in message news:<08B08C9FA5EBD311A2CC009027D5BF8102E2B618@remailnt2-re01.westat.com>...
> Roger,
>
> You appear to be reacting to a part of my response, "Macros generate text!
> That is all there is." Please remember the context as shown by the subject
> line. In that context I think my statement stands and best describes the
> situation. In general, I still think it best describes why one must learn
> macro, although I agree that it is not the whole story.
>
> I agree with your 7 points and see them as an important reminder on **how**
> macro produces something. But what is produced? Just text. Everything
> else is a side affect. I think that you are correct in that sometimes the
> side affects are far more important than the product of a macro execution.
> However they should not be confused with what is produced.
>
> Now let's look at your example of finding the area of a rectangle with
> integer valued dimensions. If I have, say a modest 100,000 rectangles, do
> you seriously propose that macro is a reasonable way to obtain the results?
> I hope not. From my point of view, while a macro can do the job, it is not
> a good example of why one should learn macro.
>
> I found your example with PEEKS etc. interesting, but rather frightening. I
> added a few parameters and statements to test the idea.
>
> %macro area(length,width,Area=,skipput=1,step=0);
>
> %local answer ln;
>
> %if not &skipput %then %put sysparm=&sysparm ;
> %let sysparm=%sysfunc(addr(sysparm));
>
> %let answer=%left(%eval(&length * &width ))NN;
>
> %let ln=3;
>
> %syscall poke(answer,sysparm,ln) ;
> %put local address: sysparm=&sysparm ;
> %put local Area: %sysfunc(peekc(&sysparm+&step,3));
>
> %mend area;
>
> The following test has mixed results.
>
> %let step = 0 ;
> %let syparm= ;
> %area(30,7,Area=sysparm,skipput=1,step=&step);
> %put global address: &sysparm ;
> %put global Area: %sysfunc(peekc(&sysparm+&step,3));
>
> %let syparm= ;
> %area(30,7,Area=sysparm,skipput=0);
> %put global address: &sysparm ;
> %put global Area: %sysfunc(peekc(&sysparm+&step,3));
>
> In the first block things look fine. In the second block a simple %PUT
> destroyed the results. Before putting too much weight on this technique, I
> would want to know why with some ability to predict such results. Perhaps
> you or Paul can step forward with more information on when it is safe to
> use.
>
> The STEP parameter has a mild interest for values say 1 to 8. Perhaps one
> should write a macro to perform a hex dump of the area before proceeding too
> far. Have you used this tool in any serious fashion?
>
> Thanks Roger, while I mean to be critical, I also think your message an
> important one. Thanks also for alerting me several years ago to how
> efficiently %SYSFUNC evaluates functions. It came as a great shock to me.
>
> IanWhitlock@westat.com
> -----Original Message-----
> From: Roger DeAngelis [mailto:xlr82sas@AOL.COM]
> Sent: Sunday, December 22, 2002 10:55 AM
> To: SAS-L@LISTSERV.UGA.EDU
> Subject: Re: return value from macro?
>
>
> Hi All,
>
> The macro language is more than just a 'text' generator.
>
> Unfortunately, it cannot update a SAS dataset,
> SAS has not exposed that SCL functionality.
>
> However, the macro language can:
>
> 1. Read SAS datasets.
>
> 2. Read and write flatfiles.
>
> 3. Do 'floating' point arithmetic and integer arithmetic.
> ( datastep, sql do only floating point arithmetic)
>
> 4. Do 'GUI Text'hybrid frontends.
>
> 5. Do very powerful scripting ( like PERL)
>
> 1. Macros can be executed by function keys
> 2. Macros can be executed on the command line
> 3. SAS composite statements ( /stmt )
>
> 6. Macros have the ability to determine the
> caller macro. ( a concept foreign to the datastep )
> This should increase '%windows' functionality.
>
> 7. Execute SCL, system, error checking and logging, in
> a much more natural way than the datastep language.
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> /*---------------------------------------------*\
> | |
> | Return arguments from macros. The object is |
> | to pass data or variables back to the |
> | calling program. The data or variables are |
> | placed in the scope of the calling program |
> | Global variables are created here because the |
> | calling program is in open code. Wrap a |
> | macro around the calling program and the |
> | macro variables are in the scope of |
> | the calling program. |
> | |
> | |
> | We would like to do something like this |
> | |
> | %Area(2,4,Area); |
> | Run; |
> | %Put Area=&Area; |
> | |
> | and get |
> | |
> | Area=8 |
> | |
> | and have Area local to the calling pgm scope |
> | |
> | Solution: |
> | |
> | %Macro Area(length,width,Area); |
> | |
> | %let Area=%eval(&length.*&width.); |
> | |
> | Data _null_; |
> | |
> | call symput('Area',putn(&Area,2.-l)); |
> | %Mend; |
> | |
> | However thisrequires an incomplete data |
> | step and a strategic run statement; |
> | |
> | |
> | |
> \*---------------------------------------------*/
>
> *==========================================================;
>
> * Symput Method
>
> %AreaSymput(2,4,Area);
> Run;
> %Put Area=&Area;
>
> Result
>
> Area=8
>
>
> %Macro AreaSymput(length,width,Area);
>
> %let Area=%eval(&length.*&width.);
>
> Data _null_; call symput('Area',put(&Area.,2.));
>
> %Mend AreaSymput;
>
>
> *==========================================================;
>
> * Sysparm Method for multiple arguments;
>
> %sqftvol(30,7,3,Sysparm=Area Volume);
>
> %Let Area=%sysparms(1);
> %Let Volume=%sysparms(2);
>
> %put area=&area volume=&volume;
>
> Result
>
> area=210 volume=630
>
>
> %macro sysparms(idx);
> %scan(&sysparm,&idx);
> %mend sysparms;
>
> %macro sqftvol(length,width,height,sysparm=);
>
> %let sysparm=
> %left(%eval(&length * &width )) %left(%eval(&length * &width * &height
> ));
>
> %mend sqftvol;
>
>
> *==========================================================;
>
> * Peek Poke Method can be set up without using
> a global sysparm macro variable. ( untested );
>
> %area(30,7,Area=sysparm);
> %put Area= %sysfunc(peekc(&sysparm,3));
>
> %macro area(length,width,Area=);
>
> %local answer ln;
>
> %let sysparm=%sysfunc(addr(sysparm));
>
> %let answer=%left(%eval(&length * &width ))NN;
>
> %let ln=3;
>
> %syscall poke(answer,sysparm,ln) ;
>
> %mend area;
>
> paul_dorfman@HOTMAIL.COM (Paul Dorfman) wrote in message
> news:<F77TqfnfEnimtH4ismF0000474f@hotmail.com>...
> > Nick,
> >
> > From the number of responses you have received (including the
> super-tutorial
> > by Ian) I am sure you know what is principally wrong with the way you have
> > been trying to do what you need. I would just like to add two
> considerations
> > that seem to be relevant in your situation.
> >
> > 1. It is not quite necessary to use another SAS step (SQL or else)
> preceding
> > one that calls for the statistic because nothing precludes from computing
> it
> > in the calling step itself and storing it in a regular SAS variable for
> > furhter use. Here is a framework:
> >
> > data test2 () ;
> > if _n_ = 1 then do ;
> > drop ____: ;
> > do until (____eof) ;
> > set test end = ____eof ;
> > if missing (v1) then continue ;
> > ____sum ++ v1 ;
> > ____n ++ 1 ;
> > end ;
> > if ____n then mean = ____sum / ____n ;
> > end ;
> > set test ;
> > if mean > 2 then error = mean ;
> > run ;
> >
> > Now, the entire mean-getting piece can be parameterized and encapsulated
> > using a macro, e.g.:
> >
> > %macro GetMean (data=_last_, var=, meanvar=) / stmt ;
> > do ;
> > drop ____: ;
> > do until (____eof) ;
> > set &data end = ____eof ;
> > if missing (&var) then continue ;
> > ____sum ++ &var ;
> > ____n ++ 1 ;
> > end ;
> > if ____n then &meanvar = ____sum / ____n ;
> > end ;
> > %mend GetMean ;
> >
> > Then the macro can be called as a *routine* to obtain the mean of the
> > variable *you* name:
> >
> > data test2 () ;
> > if _n_ = 1 then %GetMean (data=test, var=v1, meanvar=mean) ;
> > set test ;
> > if mean > 2 then error = mean ;
> > run ;
> >
> > Note that it is also you who indicates the input data set and the variable
> > where the mean value returned by the macro-generated code ought to be
> > stored. This entire calling style actually conforms to that of SAS, which
> > becomes especially noticeable if you have the option IMPLMAC in effect and
> > call the routine as (for demonstration purposes only, I do not recommend
> > this style of calling in general)
> >
> > data test2 () ;
> > if _n_ = 1 then GetMean data=test var=v1 meanvar=mean ;
> > set test ;
> > if mean > 2 then error = mean ;
> > run ;
> >
> > Now GetMean really looks like a SAS statement with options. Why the
> > disclaimer above? Because when the macro is called statement-style, the
> > macro processor has to scan each token in the program to find out if it
> > spells GetMean. Aside from the performance issue (which actually not as
> big
> > a deal as some folks tend to think), imagine that in the same program,
> > another token (for example, a variable or data set name, or what have you)
> > is called GetMean as well! Calling the macro standard style means less
> work
> > for the processor and does not create the problem indicated above in the
> > first place.
> >
> > Regardless of the calling style, this approach allows for the macro
> > flexibility somewhat different from one produced by having two steps - one
> > passing, and one receiving - wrapped in a macro.
> >
> > 2. When a numeric value whose accuracy may be significant is transported
> to
> > the calling step via a macro variable, it is accompanied by the issue of
> > precision, which never arises if the value is computed in the same step as
> > suggested above or transported via a 1-obs SAS file. For example, if SQL
> is
> > used to obtain the mean as
> >
> > data test;
> > input v1 v2;
> > cards;
> > 2 3
> > 2 1
> > 3 2
> > 1 7
> > 4 2
> > 1 1
> > ;
> > run ;
> > proc sql noprint ;
> > select avg (v1) into: mean from test ;
> > quit ;
> > %put &mean ;
> > ------------
> > 2.166667
> >
> > it will pass this character string to the consuming step, resulting in the
> > lost of precision chopped off by the default format best8. Using best32.
> > will partially remedy the situation, but really the best way is to pass
> the
> > real-binary numeric value completely unchanged. The only way to do it via
> a
> > macro variable (Hey Mad Doggy, ready for another piece of cheese?)
> > is:
> >
> > proc sql noprint ;
> > select quote (put (put(avg (v1), rb8.), $hex16.)) into: mean from test
> ;
> > quit ;
> > data test2 () ;
> > set test ;
> > mean = input (input(&mean, $hex16.), rb8.) ;
> > if mean > 2 then error = mean ;
> > run ;
> >
> > This way, the stuff passed through the macro variable is nothing but the
> > quoted string "5555555555550140" which is the exact ASCII hex
> representation
> > of the bytes composing the actual real-binary value. Why not pass the RB8.
> > string itself? Because it is likely to contain macro triggers, and it is
> > better to pass a string that is guaranteed to be alphanumeric-only instead
> > of engaging in the macro quoting business.
> >
> > After all that, if one decides for a 2-step process, is it not much
> simpler
> > just to write and read a 1-obs file? Say,
> >
> > proc sql ;
> > create table mean as select avg (v1) as mean from test ;
> > quit ;
> > data test2 () ;
> > if _n_ = 1 then set mean ;
> > set test ;
> > if mean > 2 then error = mean ;
> > run ;
> >
> > Kind regards,
> > ------------------
> > Paul M. Dorfman
> > Jacksonville, FL
> > ------------------
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> > ----Original Message Follows----
> > From: N Yiannakoulias <nwy@SRV.UALBERTA.CA>
> >
> > Hi all,
> >
> > Is it possible to have macros return a value? If so, what
> > am I doing wrong here?
> >
> > %MACRO getmean(the_data, v);
> > PROC SQL;
> > SELECT sum(&v)/count(&v) INTO: return
> > FROM &the_data;
> > QUIT;
> > &return;
> > %MEND;
> >
> > DATA test;
> > INPUT v1 v2;
> > CARDS;
> > 2 3
> > 2 1
> > 3 2
> > 1 7
> > 4 2
> > 1 1
> > ;
> > DATA test2;
> > SET test;
> > IF %getmean(test, v1) > 2 THEN DO;
> > error=%getmean(test, v1);
> > END;
> > RUN;
> >
> > N
> >
> >
> > _________________________________________________________________
> > The new MSN 8: smart spam protection and 3 months FREE*.
> >
> http://join.msn.com/?page=features/junkmail&xAPID=42&PS=47575&PI=7324&DI=747
> 4&SU=
> >
> http://www.hotmail.msn.com/cgi-bin/getmsg&HL=1216hotmailtaglines_smartspampr
> otection_3mf
|