Conditionally Executing Global Statements

From sasCommunity
Revision as of 08:17, 9 June 2015 by Ron.Fehd.macro.maven (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

The IFC function can be used to conditionally execute global statements, including macro %Put, %Let, and %Include.

To refer to this page use: http://tinyurl.com/25ba6l

Demonstration of Function IFC in Data Step

DATA   Testing;
attrib Msg length = $ %length(missing);
do I = ., 0, 1, -1;
   Msg = ifc(I,'true','false','missing');
   put I= Msg=;
   end;
stop;
run;

log:

3          DATA Testing;
4          attrib Msg length = $ %length(missing);
5          do I = ., 0, 1;
6             Msg = ifc(I,'true','false','missing');
7             put I= Msg=;
8             end;
9          stop;
10         run;

I=. Msg=missing
I=0 Msg=false
I=1 Msg=true
I=-1 Msg=true

Open Code Macro Statements

options source2;
%Put 
%sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
            ,source2: echo includes
            ,nosource2: no echo of includes
         )  );

options nosource2;

%Put 
%sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
            ,source2: echo includes
            ,nosource2: no echo of includes
         )  );

%Let Mvar = 
%sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
            ,source2: echo includes
            ,nosource2: no echo of includes
         )  );
%Put Mvar: &Mvar.;

log:

2          options source2;
3          %Put
4          %sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
5                      ,source2: echo includes
6                      ,nosource2: no echo of includes
7                   )  );
source2: echo includes
8          
9          options nosource2;
10         
11         %Put
12         %sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
13                     ,source2: echo includes
14                     ,nosource2: no echo of includes
15                  )  );
nosource2: no echo of includes
16         
17         %Let Mvar =
18         %sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
19                     ,source2: echo includes
20                     ,nosource2: no echo of includes
21                  )  );
22         %Put Mvar: &Mvar.;
Mvar: nosource2: no echo of includes


Executing Global Macro Statements

Take a look at the obvious.

options source2;
%sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
            ,%put true*;
            ,%put *false;
            ))

log: Ooops!

2          options source2;
3          %sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
4                      ,%put true*;
true*
5                      ,%put *false;
*false
6                      ));


Using Nrstr to Delay Execution of Macro Statements

Fehd and Carpenter review the timing issues of using %nrstr to delay execution of macro statements until they are popped off the stack.

options source2;
%sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
            ,%nrstr(%put true*;)
            ,%nrstr(%put *false;)
            ));
options nosource2;
%sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
            ,%nrstr(%put true*;)
            ,%nrstr(%put *false;)
            ));

log:

2          options source2;
3          %sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
4                      ,%nrstr(%put true*;)
5                      ,%nrstr(%put *false;)
6                      ));
true*
7          options nosource2;
8          %sysfunc(ifc(%sysfunc(getoption(source2)) eq SOURCE2
9                      ,%nrstr(%put true*;)
10                     ,%nrstr(%put *false;)
11                     ));
*false

The above code can be used to conditionally execute %includes.

Note This code can be used to do Assertions.

Trick with Macro Variable Resolution

All macros are resolved. For macro variables, resolved means substituting. For macro definitions, resolved means expanding.

This trick involves in-line expansion of a conditionally-executed string.

Knowledge to remember:

  •  %nrstr (no-rescan-this-is-a-string) function disables substitution of macro variable reference(s) (&mvar)

and expansion of macro definition(s) (%verb(data=)).

  •  %unquote function directs the macro language to put the string in the token-resolution stream;

i.e. macro variables reference and definitions are resolved.

%let macro_catalog=work.sasmacr;
%sysfunc(ifc(  %sysfunc(cexist(&macro_catalog))
    ,%nrstr(%put exist catalog(&macro_catalog);)
,%nrstr(%put not exist catalog(&macro_catalog);),))
 
%macro test1(data=sashelp.class);
*noop;
%mend;
run;
%put echo %sysfunc(ifc(%sysfunc(cexist(&macro_catalog))
        ,,%nrstr(not),)) exist catalog(&macro_catalog);
 
**** macros compiled and stored;
%put mstored=%sysfunc(getoption(mstored));
%let sasmstore=%sysfunc(getoption(sasmstore));
%put &=sasmstore;
 
* if not already allocated;
*libname library '.';
options mstored sasmstore=library;
%put mstored=%sysfunc(getoption(mstored));
%let sasmstore=%sysfunc(getoption(sasmstore));
%put &=sasmstore;
 
%let exist = cexist;*catalog;
%let object = &sasmstore..sasmacr;
%let exist_note = %nrstr(echo
%sysfunc(ifc(%sysfunc(&exist(&object))
        ,,%nrstr(not),)) exist object(&object));
 
%put %unquote(&exist_note);
 
%macro test2(data=sashelp.class)/store;
*noop;
%mend;
 
%put %unquote(&exist_note);
 
%let object = work.formats;
%put %unquote(&exist_note);
%let object = library.formats;
%put %unquote(&exist_note);
proc format library=library;
value demo 1='one';
run;
%put %unquote(&exist_note);
 
 
%let exist = exist;*data set;
%let object = sashelp.class;
%put %unquote(&exist_note);
%let object = work.class;
%put %unquote(&exist_note);

The log is left as an exercise for the reader.

Assertions

Reference: Automated Testing and Real-time Event Management

http://www2.sas.com/proceedings/sugi29/228-29.pdf

  • boolean: zero, one, or ge one
    • exist(Data)
    • fexist(FileRef)
    • nobs(Data)
    • nvars(Data)
  • comparison:
    • &NobsData. eq &NobsFreq.
    • &Nobs1. and &Nobs2.
  • existence:
    • exist(data),
    • fexist(fileref)

Assert Exist Data

OnLine Doc: exist

http://support.sas.com/documentation/cdl/en/lrdict/59540/HTML/default/a000210903.htm

%sysfunc(ifc(%sysfunc(exist(&Data.))
            ,%nrstr(%Put Note2: Data &Data. exists;)
            ,%nrstr(%Put Note3: not exist &Data.;
                    endSAS;) ))
* assert-exist-data-Test;
options source2;
%let data = sashelp.class;
%Include Project(assert-exist-data);
%let data = sashelp.classX;

Assert FExist FileRef

OnLine Doc: fexist

http://support.sas.com/documentation/cdl/en/lrdict/59540/HTML/default/a000210817.htm

%sysfunc(ifc(%sysfunc(fexist(&FileRef.))
            ,%nrstr(%Put Note2: FileRef &FileRef. exists;)
            ,%nrstr(%Put Note3: not exist &FileRef.;
                    endSAS;) ))
* assert-fexist-fileref-Test;
options source2;
%let Fileref = SiteIncl;
%Include Project(assert-fexist-fileref);
%let Fileref = SiteInc;
%Include Project(assert-fexist-fileref);

Assert Exist FileNameExt

OnLine Doc: fileexist

http://support.sas.com/documentation/cdl/en/lrdict/59540/HTML/default/a000210912.htm

%sysfunc(ifc(%sysfunc(fileexist(&FileNameExt.))
            ,%nrstr(%Put Note2: FileNameExt &FileNameExt. exists;)
            ,%nrstr(%Put Note3: not exist &FileNameExt.;
                    endSAS;) ))
* assert-fileexist-filename-Test;
options source2;
%let Fileref = SiteIncl;
%Include Project(assert-fileexist-filename);
%let Fileref = SiteInc;
%Include Project(assert-fileexist-filename);

Assert FileRef

The fileref function is an exception to the returns boolean rule.

  • -1: A negative return code indicates that the fileref exists
    • but the physical file associated with the fileref does not exist.
  • 0 : A value of zero indicates that
    • the fileref and external file both exist.
  • +1: A positive value indicates that the fileref is not assigned.

OnLine Doc for Windows:

http://support.sas.com/documentation/cdl/en/hostwin/59544/HTML/default/win-func-fileref.htm

%sysfunc(ifc(%sysfunc(fileref(&FileRef.)) eq 0
            ,%nrstr(%Put Note2: FileRef &FileRef. exists but file not;)
            ,%nrstr(%Put Note3: not exist &FileRef.;
                    endSAS;) ))

see OnLine Doc of Windows finfo function for a usage example.

http://support.sas.com/documentation/cdl/en/hostwin/59544/HTML/default/win-func-finfo.htm

* assert-fileref-Test;
* see OnLine Doc of finfo function for usage example;
options source2;
filename ThisFile 'assert-fileref-Test.sas';
%let Fileref = ThisFile;
%Include Project(assert-fileref);
filename NextFile 'assert-fileref-Test.txt';
%let Fileref = NextFile;
%Include Project(assert-fileref);

Assert LibRef

The libref functions returns:

  • 0: success == exists
  • #0: failure
%Let Libref = LIB5;
%Let Libref = sashelp;
 
%sysfunc(ifc(%sysfunc(libref(&Libref.))
            ,%nrstr(%Put note: &Libref. not exists;)
            ,%nrstr(%Put note: &Libref. exists;)
         )  )

Assert Nobs or Nvars

OnLine Doc: open

OnLine Doc: attrn: nobs and nvars

OnLine Doc: close

%let dsid  = %sysfunc(open(&Data. ));
%let Nobs  = 0;
%let Nvars = 0;
%sysfunc(ifc(&DsId.
            ,%nrstr(%let Nobs  = %sysfunc(attrn(&dsid.,nobs ));
                    %let Nvars = %sysfunc(attrn(&dsid.,nvars));)
            ,%nrstr() ))
%let rc    = %sysfunc(close(&dsid.)); 
%sysfunc(ifc(&Nobs.
            ,%nrstr(%Put Note2: Data &Data. available Nobs: &Nobs.;
                    %Put Note2: Data &Data. available Nvars: &Nvars.;
                    )
            ,%nrstr(%Put %sysfunc(sysmsg());
                    endSAS;) ))
options source2;
%Let Data = sashelp.Class;
%Include Project(assert-nobs);
%Let Data = sashelp.ClassX;
%Include Project(assert-nobs);

Branching

A version of this trick was shown in: The Writing for Reading SAS ® Style Sheet: Tricks, Traps & Tips from SAS-L’s Macro Maven

http://www2.sas.com/proceedings/sugi25/25/ad/25p038.pdf

This example shows testing for nobs and branching on Nobs

  • ge 0
  • eq 0
* cond-inc-which;
options source2;
%Let Data = sashelp.Class;
%Let Data = sashelp.ClassX;
%let dsid = %sysfunc(open(&Data. ));
%let Nobs =
%sysfunc(ifc(&DsId.
            ,%nrstr(            %sysfunc(attrn(&dsid.,nobs));
                    %let rc   = %sysfunc(close(&dsid.)); )
            ,%nrstr(0) ));
%sysfunc(ifc(&Nobs.
            ,%nrstr(%Include Project(cond-inc-1);)
            ,%nrstr(%Include Project(cond-inc-0);) ))
%Put Note2: file is cond-inc-0;
%Put Note2: file is cond-inc-1;

--macro maven == the radical programmer 16:23, 7 March 2008 (EST)

Be Careful of Characters that Require Quoting

This technique does not handle special characters that require macro quoting well. Consider the following example.

Set up two macro variables to be compared and then compare them using this technique:

1    %let value = a/b;
2    %let compare = some other value;

3    %put %sysfunc(ifc(&value = &compare
4                  ,Equal
5                  ,Different));
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric
       operand is required. The condition was: a/b = some other value
ERROR: Argument 1 to function IFC referenced by the %SYSFUNC or %QSYSFUNC macro function is not
       a number.
ERROR: Invalid arguments detected in %SYSCALL, %SYSFUNC, or %QSYSFUNC argument list.  Execution
       of %SYSCALL statement or %SYSFUNC or %QSYSFUNC function reference is terminated.

Since the values include the / character, a numeric operation is implied. To address this in the macro language, one would use a macro quoting function:

6    %put %sysfunc(ifc(%bquote(&value) = %bquote(&compare)
7                  ,Equal
8                  ,Different));
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric
       operand is required. The condition was: a/b = some other value
ERROR: Argument 1 to function IFC referenced by the %SYSFUNC or %QSYSFUNC macro function is not
       a number.
ERROR: Invalid arguments detected in %SYSCALL, %SYSFUNC, or %QSYSFUNC argument list.  Execution
       of %SYSCALL statement or %SYSFUNC or %QSYSFUNC function reference is terminated.

Note how the error still occurs.

An IIF Macro

The following macro is referenced in the book Building Web Applications with SAS/IntrNet: A Guide to the Application Dispatcher as a way of doing such comparisons in Open Code.

9    %macro iif
10         (cond,      /* logical expression to evaluate */
11          positive,  /* text to return if expression true */
12          negative   /* text to return if expression false */
13         );
14    /*-------------------------------------------------------------------*/
15    /*            Building Web Applications with SAS/IntrNet(R):         */
16    /*            A Guide to the Application Dispatcher                  */
17    /*          by Don Henderson, Henderson Consulting Services          */
18    /*       Copyright(c) 2006 by SAS Institute Inc., Cary, NC, USA      */
19    /*                   SAS Publications order                          */
20    /*                   ISBN 978-1-59994-189-9                          */
21    /*-------------------------------------------------------------------*/
22    /* rest of comment blocked snipped                                   */
23
24    %if   &cond %then %do;
26          %unquote(&positive)
27          %end;
28    %else %do;
30          %unquote(&negative)
31          %end;
32
33   %mend IIF;

Note that when the values are not quoted, we get the same error with the IIF macro:

35   %put %iif(&value = &compare
36             ,Equal
37             ,Different);
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric
       operand is required. The condition was: &cond
ERROR: The macro IIF will stop executing.

But when quoted, we get the correct behavior as one would expect when using macro quoting functions.

38   %put %iif(%bquote(&value) = %bquote(&compare)
39             ,Equal
40             ,Different);
Different

References

See also: Assertions