(注:由于有部分代码和啰嗦的程序解释,所以导致帖子很长,不得不分两层贴出)
关于 CALL EXECUTE()
CALL EXECUTE() 的功能是"Resolves its argument and executes the resolved value at the next step boundary"。
CALL EXECUTE()的参数是字符串,在执行时,SAS会把这个字符串送到宏处理器中。宏处理器如遇到宏的语句,如%let、%put等,会直接执行;如遇到SAS语句,这些SAS语句会被排到CALL EXECUTE()所在DATA步之后等待运行。
如果使用熟练,CALL EXECUTE()实在是称得上一件趁手的兵器,值得收在SAS武器库里备用。当然,这会需要多加练习和揣摩。希望下面的几个例子能够帮助大家理解CALL EXECUTE()的执行机制,以避免掉入陷阱。或者即便是使用CALL EXECUTE()时遇到了血红色的“ERROR”,也能很快的找到错误的原因。话说回来,能够给出错误或警告信息的陷阱还算是“好”的陷阱,有些陷阱是不会在log窗口留下任何痕迹的。
CALL EXECUTE() 能做什么
很多情况下,CALL EXECUTE()可以实现宏的效果。使用宏,我们需要“定义宏”+“调用宏”;使用CALL EXECUTE(),我们可以在DATA步的执行过程中,根据变量的值生成不同的代码,而这些代码会在DATA步执行完后自动运行。
比如,下面的例子可以求出数据集中数值型变量的描述性统计和字符型变量各个值的频数:
- data _null_;
- set sashelp.vcolumn;
- where libname="SASHELP" and memname="CLASS";
- *** Get Frequency table for character var;
- if type="char" then
- call execute("proc freq data=sashelp.class;
- table "||strip(name)||"/out=freq_"||strip(name)||";
- run;"
- );
- *** Get descriptive statistics for numeric var;
- else if type="num" then
- call execute("proc means data=sashelp.class;
- var "||strip(name)||";
- output out=stat_"||strip(name)||";
- run;"
- );
- run;
如果CALL EXECUTE()的参数值只包含SAS语句的话,理解起来是比较简单和直观的。但如果其参数值包含宏的因素(宏变量,宏语句,宏的调用)时,理解起来就可能比较困难,难点就在于究竟宏变量的值是什么时候解析的,以及宏语句是什么时候被执行的。
CALL EXECUTE() 中的宏变量
下面这个例子可以用来说明CALL EXECUTE()中宏变量的解析情况:
- %macro test(aa);
- data test;
- x='&aa';
- y=symget('aa');
- put x= y=;
- run;
- %mend;
- %let aa=Good;
- data _null_;
- *** Double quotation;
- call execute("%test(bad)");
- *** Single quotation;
- call execute('%test(bad)');
- run;
程序解释:
数据集TEST中变量X的值:
第一个CALL EXECUTE()所产生的数据集中 X的值为’bad’。这是因为当宏的调用放到双引号中时,宏的执行是在DATA步的编译阶段进行的。而宏执行的结果(’data test’ 代码)再放到一对双引号之间作为CALL EXECUTE() 的参数。这就相当于:
call execute("data test; x='&aa'; y=symget('aa'); put x= y=; run;"); |
这时虽然宏变量aa的引用用两边是单引号(’&aa’),但是整个字符串却处在双引号里面,因而单引号会被当成普通字符,并不会影响到&aa的解析为宏参数的值--bad。这样DATA步编译完后,第一个CALL EXECUTE() 就会是:
call execute("data test; x='bad'; y=symget('aa'); put x= y=; run;"); |
第二个CALL EXECUTE()所产生的数据集X的值为'&aa'。 这是因为宏的调用位于单引号中,所以DATA步编译阶段宏不会执行。而在DATA步的执行阶段,CALL EXECUTE() 中调用的宏会被执行,产生的SAS代码会去排队(在input stack中)等待运行。这时&aa两边的单引号就会发挥其“屏蔽宏变量解析”的功能,因此X的值即为'&aa'.
数据集TEST中变量Y的值:'Good'
因为SYMGET()函数的参数只是宏变量的名字'aa'而不用符号’&’,因此在DATA _NULL_步的编译和执行过程中都不会试图去解析宏变量aa的值。而等DATA _NULL_执行完毕,CALL EXECUTE()所产生的代码执行时,SYMGET()才去尝试找aa的值。需要注意的是,此时的代码并不在宏的执行环境里,而是在open code。所以SYMGET() 返回的aa的值不是%test宏的参数值,而是global symbol table中的值—Good。
另外,如果希望SYMGET()函数返回宏%test所指定的参数值,可以用:
call execute('%nrstr(%test(bad))'); |
a. 单引号的使用可以跳过所在DATA步的编译阶段;
b. %NRSTR()的使用可以使宏的执行跳过所在DATA步的执行阶段;
这就相当于DATA步执行完毕后直接调用宏:%test(bad)