能被大家都使用的SAS Macro编写指南
Guidelines on Writing SAS Macros for Public Use
从优秀的源码中学习应该算是一条非常好的学习SAS的途径,我们经常在网上看到一些公开的且优秀的源码。今天我们就来讲一些SAS Macro编写的规则,有了这些规则,你的SAS代码将更容易被别人读懂,从而让更多的人学习和重用。
1 文档化
文档化一直是很重要的东西,特别是对SAS Macro,因为只有这样才有更多的人知道这些Macro如何才能正地确地执行,并且结果是他们所需要的。下面介绍由Whitney (1996)提出来的SAS Macro的文档模板:
上面这些信息可以根据实际情况选择性地列举出来。
其次,为了使代码更有可读性,我们还可以将功能、变量等名字取得更具清晰明白,从而让人能根据其名字就知道这个函数或变量是干什么用的。例如要创建一个重复的数据集,我们可以用PRINT_DUPS来命名这个数据集,而不是用CheckData来命名,还有就是可以用SAS的保留字来作用后缀或前缀等,例如,SAS Macro里的数据集参数,我们可以用In_Data或Out_Data等,这样我们就很清楚地知道这是数据集,其它的如Keep、Where、Nobs等。一个非常重要的一点是命名一定要有连续性,也即风格要一致。
最后就是SAS Macro的函数或变量最好用描述性的名字命名,这样在后期的Debug或应用时都能帮助理解,例如一般不用i或j等一般性的变量来命名,取而代之以year,visit等来命名。
另外我们还可以加一些选项来对宏进行Debug,这些选项如MPRINT,MLOGIC和SYMBOLGEN等,以及用%put进行Debug。这一块我们讲专门介绍如何Debug Sas Macro。
2获取错误信息
我们可以被动地通过SAS自己的错误提示来得到错误信息,另一方面,我们可以主动地写一些代码来获得错误信息。当然我们很难预测并获取所有的错误信息,但是有一些错误是很标准化的,这些错误我们可以尽量自己获取。例如我们可以通过%LENGTH来判断宏变量是否被指定:
%if %length(&data) = 0 %then %do ;
%end ;
%finish:
这样,如果没有宏变量,则其length将为空或0,这样就会生成一条错误信息并结束过程。
另一个例子是判别是否指定的数据集存在:
%let error = 0 ;
%if %sysfunc(exist(&data)) = 0 %then %do ;
%end ;
%if &error = 1 %then %goto finish ;
%finish:
这些例子都比较简单,我们在应用于的时候可能会遇到更复杂的情况。其它的应用包括判别变量是否为字符型,是否库名或格式合法等等。为了让主Macro不显得太复杂,我们可以写一些子宏来完成这些功能。
还有一个比较好的习惯是我们可以预先赋给宏变量一些默认值,而不是完全由用户来赋值。并且我们还可以增加赋值的变化来减少输入值可能的错误,例如:
%let debug = %upcase(&debug) ;
通过将debug全部转化为大写,从而用户可以输入任意的大小字母,对结果都无影响。另一个例子:
%let debug = %upcase( %substr(&debug,1,1 ) ) ;
我们只取第一个字母,且将其大写,这样,用户可能多输入几个字母,但对结果亦无影响。
3 减少一些可能引起歧议的句子
由于程序总会以程序员没有考虑到的方式运行,比如建立了一个已存在的数据集,这样就很可能将原来的数据集覆盖了。因为,一个简单的方式是我们建立数据集时,一定要先检查一下是否已存在这个数据集,另外,对于Macro生成的数据集,当我们不再需要它时,一定要将其删除。
另一个经常发生的问题是全局宏变量,在Macro中,其值被修改了:
%let x = 5 ;
%macro check ;
%* %local x ;
%let x = 1 ;
%mend check ;
%check
%put x = &x ;
这里的x为1而不是5。这就是因为全局宏变量x的值在宏check里被改为了1。
因此我们在创建全局宏变量时,一定要取一个比较唯一的名字,如前面加_,而局部宏时,我们最好用%Local显性定义。对于宏变量的范围问题前面已有讨论,这里就不再具体介绍了。
还有一个要注意的地方就是title和footnote用完后一定要注意将其关闭掉:
title1 'Original title';
%macro test ;
proc sql noprint;
quit ;
%let num = #
%let text = &text ;
title1 'Macro title';
title&num "&text";
%mend test;
这里最后的title1为'Original title'而不是'Macro title',因为先前定义的title1的值还被宏保存着。
4 好用
好用这个属性太重要了哈。我们介绍一下宏参数主要有两种方式:positional和keyword
%macro recode (data, out, var, groups );
%macro recode (data =, out =, var =, groups = );
其引用方式为:
Keyword比positional有很多好处:
首先Keyword更直观,我们可以很清楚地知道sales是一个数据集,revenue为一个变量,从而减少错误
其次我们可以先定义一些默认值,例如
%macro recode(data=, out=, var, groups = 5) ;
我们就可以只对前三个变量进行赋值了
在写宏时,我们一定要让宏变量尽量少,因为宏变量太多就太复杂。如果宏变量确实多,那就看看能不能给一些变量赋予默认值。
写宏时另一个好习惯是对宏变量进行注释:
%print_vars (data =
另外,我们还要尽量减少没有%的宏,例如:
options implmac ;
data test ;
run;
%macro printdat(data) / stmt ;
%mend printdat ;
printdat test ;
这个宏看起来费劲,并且还没实现什么功能,并且宏里面没有一个%,所以这不是一个很好的编程习惯
5 权衡与思考
首先是当我们写宏时,也要考虑一下大家的实际情况,比如在第九版我们创建一个宏变量,可以简单地用call symputx ('count', count) ;实现,但在第6版时,完整代码是call symput ('count', trim(left(put(count,8.)))) ;,其它情况包括我们可能有graph qc or ets等产品,而有的却只有base,这时你写的代码可能就不会被这些人使用。
另一个宏的重要特征是robust,就是可以用到很多平台上,但这样会浪费我们很多时间,这时,我们最好根据实际情况来定是否需要写这样的宏。
最后是一些思考,供大家参考:
(1)对文档标准,变量命名习惯等要保持连续性
(2)在发布程序之前一定要完整地测试你的代码,而不能仅仅debug。要完整地测试其功能和效率。作者举了个例子,他写了个proc freq的宏,测试时没事,但用在大数据量操作时,就死掉了,结果只能重写。
(3)一定要跟你的客户有个良好的交流,了解客户真正需要什么功能,不然写一个没人用的宏是没有意义的
(4)要将公开的宏放在一个只读的文件夹下,这样就能避免对其不经意的更改
(5)定期重新看看你写的宏,这样可能会想到更好的方式来修改你的宏。
0 comments:
Post a Comment