SAS中文论坛

 找回密码
 立即注册

扫一扫,访问微社区

查看: 851|回复: 0
打印 上一主题 下一主题

A macro design pattern by PROC FCMP

[复制链接]

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
楼主
 楼主| 发表于 2011-8-25 14:00:06 | 只看该作者

A macro design pattern by PROC FCMP

From Dapangmao's blog on sas-analysis

<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-UMLvE1BqFfU/TlXXn13L_DI/AAAAAAAAAvQ/UIzexhKqEsE/s1600/adapter.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="301" src="http://2.bp.blogspot.com/-UMLvE1BqFfU/TlXXn13L_DI/AAAAAAAAAvQ/UIzexhKqEsE/s400/adapter.png" width="400" /></a></div><br />
We all heard horrible stories that someone tried to piece together a bunch of nice functioning macros for a big macro, and ended up with a failed and undebuggable system. Part of reasons can be about encapsulation: not all SAS programmers have the good habit to localize the macro variables by %local statement; the leaking macro variables may ruin the attempt to utilize multiple macros written by different SAS programmers. To solve this problem, Mark Tabladillo brought the concepts of <a href="http://analytics.ncsu.edu/sesug/2004/TU11-Tabladillo.pdf">encapsulation, polymorphism, and inheritance</a> into nested macros. And he raised several <a href="http://www2.sas.com/proceedings/sugi31/173-31.pdf">design patterns</a> for macros to emulate the object-oriented languages.<br />
<br />
The FCMP procedure, besides its original purpose as a function compiler, could encapsulate macros by <a href="http://support.sas.com/resources/papers/proceedings10/326-2010.pdf">its RUN_MACRO function</a>. The macro-based functions seem like more safe modules than the macros themselves. Erin, Daniel and Himesh in <a href="http://support.sas.com/resources/papers/proceedings11/291-2011.pdf">their SAS Global 2011 paper </a>showed an example to build a complicated reporting system for academic performances. Their principle is to construct a few macro-embedded functions by PROC FCMP and then incorporate them with an interface macro. Here I modified their codes a little to increase the number of macros and showed the relationship among the elements in the UML diagram above. The stucture is similar to the adapter pattern, one of the many OOP design patterns, with PROC FCMP as a wrapper.<br />
<br />
Overall, functionalizing our macros or our colleagues’ macros by PROC FCMP is an alternative way to integrate them for a ‘big’ purpose.<br />
<br />
<pre style="background-color: #ebebeb; border: 1px dashed rgb(153, 153, 153); color: #000001; font-size: 14px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>

/*******************READ ME*********************************************
* THE CODES BELOW ARE COPIED AND MODIFIED FROM ERIN LYNCH, DANIEL
* O’CONNOR, HIMESH PATEL OF SAS INSTITUTE
*
* THE ORIGINAL CODE AND RAW DATA CAN BE FOUND FROM THEIR PAPER
* MY REPORTING REQUIRES A FULL STAFF—HELP!
* PAPER 291, SAS GLOBAL FORUM 2011
* support.sas.com/resources/papers/proceedings11/291-2011.pdf
*
****************END OF READ ME******************************************/

%macro tabledata_prep;
   options topmargin=.125in bottommargin=.125in leftmargin=.25in rightmargin=.25in nodate nonumber;  
   title; footnote;
   ods escapechar="~";
   %let tabledata=%sysfunc(dequote(&amp;tabledata.));
   data tabledata;
      set &amp;tabledata;
      district=substr(district,1,8)||' '||substr(district,9,1);
      school=substr(school,1,6)||' '||substr(school,7,1);
   run;
%mend;

%macro linedata_prep;
   %let linedata=%sysfunc(dequote(&amp;linedata.));
   ods _all_ close;
   data linedata;
      set &amp;linedata;
      district=substr(district,1,8)||' '||substr(district,9,1);
   run;
   proc sort data= linedata out=sorted_linedata;
      by district year;
   run;
   proc sort data= linedata out=districts(keep=district) nodupkey;
      by district;
   run;
%mend;
                                       
proc template;
   define style Styles.Charter;
      parent = styles.printer;
      style Body from Document
         "Undef margins so we get the margins from the printer or SYS option"  /
            marginbottom = _undef_
            margintop = _undef_
            marginright = _undef_
            marginleft = _undef_
            pagebreakhtml = html('PageBreakLine')
            backgroundimage="Your.png";
   end;
run;

%macro Newfile;
  %if &amp;path ne '' %then %let pathopt= path=&amp;path(url=none);
   %else  %let pathopt=;

   %if &amp;gpath ne '' %then %let gpathopt= gpath=&amp;gpath(url=none);
   %else %let gpathopt=;

   %let path=%sysfunc(dequote(&amp;path.));
   %let gpath=%sysfunc(dequote(&amp;gpath.));
   %let destination=%sysfunc(dequote(&amp;destination.));
   %let file=%sysfunc(translate(%sysfunc(dequote(&amp;file.)), "_", " "));
   %let extension=%sysfunc(dequote(&amp;extension));      

   %if &amp;styleparm ne '' %then %let styleopt= style=%sysfunc(dequote(&amp;styleparm.));
   %else  %let styleopt=;

    %if ( %upcase(&amp;destination) eq PDF ) %then %do;
       ods &amp;destination file="&amp;path.&amp;file..&amp;extension" notoc startpage=no
       &amp;styleopt;
   %end;
   %else %if (( %upcase(&amp;destination) eq RTF ) or ( %upcase(&amp;destination) eq TAGSETS.RTF )) %then %do;
       ods &amp;destination file="&amp;path.&amp;file..&amp;extension" startpage=no &amp;styleopt;
   %end;
   %else %if ( %upcase(&amp;destination) eq HTML ) %then %do;
       ods &amp;destination file="&amp;file..&amp;extension" &amp;pathopt &amp;gpathopt &amp;styleopt;
   %end;
%mend;
                                                
%macro Enrollment;
   %let district=%sysfunc(dequote(&amp;district.));
   ods text="~{newline 3}";
   ods text="~{style [width=100pct font_size=26pt background=CXf4e9c9] &amp;district Enrollment By School Year}";
   ods text="~{newline 2}";
   ods text="~{style systemtitle [just=center]Enrollment by Year}";
   ods graphics / height=3in width=6in;
   proc sgplot data=sorted_linedata(where=(district="&amp;district"));
        series x=year y=students / markers
        lineattrs=(color=CX39828C pattern=SOLID thickness=3)
        markerattrs=(color=CX0000FF symbol=STARFILLED) name='series';
   run;
%mend;

%macro District_Makeup;
   %let district=%sysfunc(dequote(&amp;district.));
   ods text="~{newline 6}";
   ods text="~{style [width=100pct font_size=26pt background=CXf4e9c9]Current Year Percentage Of Students By School}";
   proc report data=tabledata(where=(district="&amp;district")) nowd
         style(report)={frame=void font_size=12pt rules=none backgroundcolor=CXF4E9C9
         cellpadding=0 cellspacing=0};
      define district / noprint;
      define students / noprint;
      define total_enrollment / noprint;
      define school / '' style(column)={width=5.5in};
      define percent / '' style(column)={width=.5in} right;
   run;
%mend;

%macro Closefile;
   %let destination=%sysfunc(dequote(&amp;destination.));
   ods &amp;destination close;
%mend;

proc fcmp outlib=work.fncs.submit;
   function tabledata_prep(tabledata $);
      rc = run_macro('tabledata_prep', tabledata);
      return(rc);
   endsub;
   function linedata_prep(linedata $);
      rc = run_macro('linedata_prep', linedata);
      return(rc);
   endsub;
   function Enrollment(district $);
      rc = run_macro('Enrollment', district );
      return(rc);
   endsub;
   function District_Makeup(district $);
      rc = run_macro('District_Makeup', district );
      return(rc);
   endsub;
   function Newfile( destination $, path $, gpath $, file $, extension $, styleparm $ );
      rc = run_macro('Newfile', destination, path, gpath, file, extension, styleparm );
      return(rc);
   endsub;
   function Closefile( destination $ );
      rc = run_macro('CloseFile', destination );
      return(rc);
   endsub;
run; quit;

%macro Academic_Performance_Report (linedata =, tabledata = , destination=, path=, gpath=, extension=, style= );        
   options mlogic mprint;
   %if ( "&amp;extension" eq "" ) and ( &amp;destination ne "" ) %then %let extension =&amp;destination;
   options cmplib=work.fncs;      
   data _null_;
      rc = tabledata_prep(symget('tabledata'));
      rc = linedata_prep(symget('linedata'));
   run;
   data _null_;
      set districts;                    
      rc = Newfile( symget('destination'), symget('path'), symget('gpath'),
         cats(district, "_Annual_Performance"), symget('extension'), symget('style'));                                 
      if ( rc eq 0) then do;
         rc = Enrollment( district );
         rc = District_Makeup( district );
         rc = Closefile(symget('destination'));
      end;  
   run; quit;
%mend;

%Academic_Performance_Report(linedata = data1, tabledata = data2, destination=html, path=, gpath=, extension=, style=Charter );
</code></pre><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3256159328630041416-6242788859373107342?l=www.sasanalysis.com' alt='' /></div><img src="http://feeds.feedburner.com/~r/SasAnalysis/~4/uz4X-wwavfQ" height="1" width="1"/>
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|手机版|Archiver|SAS中文论坛  

GMT+8, 2025-6-9 21:45 , Processed in 0.068120 second(s), 19 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表