|
On Fri, 6 Feb 2004 07:09:38 -0800, knvsol <knvsol@COMCAST.NET> wrote:
>trying to do the following:
>%macro function ( a , b ) ;
> %if &b >0 %then /* this line is being igonered!!*
> &a/&b;
>%mend function;
> data test ;
> input x y ;
> result = %function ( x , y );
> cards ;
> 1 2
> 3 4
> 5 6
> 7 0
> ;
>proc print data=test;run;
>
>I read a lot of posting about using a macro as a 'function'. Is there
>anyway to still acommplish the above code ( check value b > 0 ) in one
>single function?
>
hi, knvsol,
IMHO, there are three different ways to think of "macro as a function."
In most cases, "macro as a function", refers to using a macro to hide a
complicated expression. There are a lot of examples, but I found two sas-l
postings, both by very eminent members of sas-l:
Age calculation macro:
http://listserv.uga.edu/cgi-bin/wa?A2=ind9707A&L=sas-l&P=R3910&D=0&m=11860
Word counting macro:
http://listserv.uga.edu/cgi-bin/wa?A2=ind9709B&L=sas-l&D=0&m=19561&P=5025
We can write a lot of interesting macro functions in this way. It will be
better if we have some branching capability -- like iif() worksheet
function in Excel or the conditional expression (?:) in C.
In another case, "macro as a function," refers to a macro that returns a
literal and to be used in the right side of %let command or in some macro
expressions. In most cases, this technique involves setting up an local
variable and assign a value, then at the end, you reference it in the
open, like:
%macro odd(number);
%let return;
%if %sysfunc(mod(&number,2))=0 %then
%let return = 1;
%else
%let return = 0;
%*;&return. /* the comment in front rids of extra space */
%mend odd;
Usage example:
%put ***%odd(3)***%odd(4)***;
/* on log
***0***1***
*/
This is one of many ways of passing parameters around between macros, too.
Now, you are asking for the third kinds that returns a value for each call
during the data step execution. With resolve() function, this gets
possible, sort of.
Besides the point about if this is a sane approach to a given problem,
there are several technical poblems:
(1) when a macro is resolved in this way, it is resolved in open code --
you are only allowed to use macro statements that are usable in the open
(pretty much only %put and %let). This rules out using any branching (%
if's %goto's) or looping (%do's); (unless we somehow use these in a macro -
- see the example below)
(2) remember that resolve() function returns a string. This means that you
have to wrap the resolve() function with input() to convert the return
values into numeric, if it returns a numeric value -- this usually means
losing at least some numeric precision.
(3) If you have some input parameters to pass to your "macro function,"
then it cannot be macro parameters (because then it will be referenced
only once when the macro is compiled.). You have to call symput them
through global macro. (I may be incorrect about this -- I would appreciate
if you let me know!)
Despite all these limitations, it is *possible* to make a "macro
function," in the third sense -- sort of. For example, the
following "macro function" checks if the denominator is zero or missing,
then only when it is not, carries away the division. It is admittedly,
very ugly, but the fact that it works demonstrates the seemingly endless
possibility of having a very flexible macro system like sas's :-)
Cheers,
Chang
<sasl:code>
%let num=;
%let den=;
%macro safeDiv;
%macro _safeDiv;
%local r;
%if &den.=0 or &den.=. %then %do;
%let r=.;
%end; %else %do;
%let r=%sysevalf(&num. / &den.);
%end;
&r.
%mend _safeDiv;
%_safeDiv
%mend safeDiv;
data one;
num = 1;
do den = -1, ., 0, 1;
call symput('num', put(num, best12.));
call symput('den', put(den, best12.));
result = input(resolve('%safeDiv'), best12.);
output;
end;
run;
proc print data=one;
run;
/* on lst
Obs num den result
1 1 -1 -1
2 1 . .
3 1 0 .
4 1 1 1
*/
</sasl:code>
|