Macro系列导读
SAS Macro作为SAS高手不可或缺的一项技能,是因为它功能足够强大,能极大的提升程序开发效率;使你的时间和精力投入在更有价值的事情上。
Macro系列文章,将会逐一奉上SAS Macro的点点滴滴,带你踏上从认识Macro到熟练使用Macro之路。掌握SAS Macro,将会使你的SAS编程能力更上一层楼。
上
期
上期文章“宏系统选项及其他”,针对宏语言体系中的最后一部分内容进行了介绍,包括:宏系统选项、DATA步中有关宏的Functions及Call Routines、PROC SQL中的有关宏的子句。至此宏语言体系中的所有内容基本已经介绍完毕。
回
顾
Macro系列(11)—Scope of Macro Variable
从本期文章开始,将对宏语言中的一些专题内容进行一一介绍。首先就是宏变量的Scope,具体就是指宏变量的可使用范围。从使用范围来看,宏变量可分为两种:全局宏变量和局部宏变量。
其中全局宏变量可以在程序中的任何地方使用;局部宏变量只存在于宏程序内部,所以只能在创建该局部宏变量的宏程序内部使用。如果有宏程序的嵌套,还会存在局部变量中的局部变量。无论多复杂的嵌套,有一条不变的规则是,在某个宏程序中创建的局部宏变量只能在当前宏程序的范围之内使用。
1. 局部宏变量
首先,所有宏程序的参数都是局部宏变量;在宏程序内部通过%LET语句、PROC SQL中的INTO子句创建的宏变量都为局部宏变量。而DATA STEP中的CALL SYMPUT比较特殊,根据不同的情况,可创建全局的宏变量,也可创建局部的宏变量(具体情况,后文有说明)。因此,一般情况下,如果要创建宏变量最好使用%LOCAL语句。
示例程序一:
%macrocreate_local;
%locallocal_var1 local_var2 local_var3;
%letlocal_var1=local macro variable1;
data _null_;
call symput('local_var2', 'local macro variable2');
run;
proc sql noprint;
select avg(age) into :local_var3
from sashelp.class;
quit;
%putinside macro: local_var1=local_var1;
%putinside macro: local_var2=local_var2;
%putinside macro: local_var3=local_var3;
%mendcreate_local;
%create_local
%putoutside macro: local_var1=local_var1;
%putoutside macro: local_var2=local_var2;
%putoutside macro: local_var3=local_var3;
日志文件:
注:即使上面程序中不使用%LOCAL语句,创建的三个宏变量LOCAL_VAR1、LOCAL_VAR2、LOCAL_VAR3依旧是局部宏变量,但建议使用%LOCAL语句。
如果不使用%LOCAL语句,而且%LET语句中的宏变量已经是一个全局宏变量,那么就会修改全局宏变量的值,而不会再创建一个宏变量。
示例程序二:
%letvar1=global macro variable1;
%letvar2=global macro variable2;
%putbefore macro: var1=var1;
%putbefore macro: var2=var2;
%macrocreate_local2;
%letvar1=local macro variable1;
%localvar2;
%letvar2=local macro variable2;
%putinside macro: var1=var1;
%putinside macro: var2=var2;
%mendcreate_local2;
%create_local2
%putafter macro: var1=var1;
%putafter macro: var2=var2;
日志文件:
宏变量存储
宏变量是存储在Symbol Table中,全局的宏变量存储在Global Symbol Table中(可通过SASHELP.VMACRO视图来查看),局部的宏变量是存储在Local Symbol Table中。因局部宏变量是临时的,只存在于宏程序执行的时候,所以Local Symbol Table也是临时的,而且每个宏程序都会对应一个Local Symbol Table。
如果宏程序之间嵌套,那么相应的Local Symbol Table也是嵌套的。外层宏程序的局部宏变量对于内层宏程序是可见的,内层宏程序的局部宏变量对于外层宏程序是不可见的。
宏程序A嵌套在宏程序B内部,因此B中的所有局部宏变量在A中是可见的;而A中的局部宏变量在B中是不可见的。对于更多层的嵌套也是一样的逻辑。
2.全局宏变量
在开始下面的内容之前,首先需要搞清楚一个名词“Open Code”。SAS程序中主要包含DATA步、PROC步、宏程序这三部分;在一个SAS程序中,宏程序之外的部分(即%MACRO语句和%MEND语句之外的部分)都称之为Open Code。
SAS系统提供的自动宏变量全部为自动宏变量(除去SYSPBUFF,该自动宏变量的值为可变宏程序的参数,在“Macro Statement(2)”中介绍%MACRO语句时有说明)。那么,如何创建一个自定义的全局宏变量呢?
下面分两部分进行说明:
如何在Open Code环境中创建;
如何在宏程序内部环境中创建;
在Open Code环境中创建
在Open Code中创建全局宏变量比较简单。通过创建宏变量的三种方式创建的宏变量都为全局宏变量。可在程序的任何地方使用,包括在宏程序内部。
使用%LET语句;
使用DATA步中的CALL SYMPUT(或者CALL SYMPUTX);
使用PROC SQL中的INTO子句;
示例程序:
*1. Use %LET Statement.;
%letgl_var1=global macro variable1;
*2. Use CALL SYMPUT in DATA Step.;
data_null_;
callsymput('gl_var2', 'global macro variable2');
run;
*3. Use INTO Clause in PROC SQL;
proc sql noprint;
selectcount(*) into: gl_var3
fromsashelp.class;
quit;
%putgl_var1=gl_var1;
%putgl_var2=gl_var2;
%putgl_var3=gl_var3;
日志文件:
打开SASHELP.VMACRO视图文件也可以看到这三个全局的宏变量信息:
在宏程序内部环境中创建
在宏程序内部创建全局宏变量有多种方式,最为简单的是首先使用%GLOBAL语句创建宏变量,然后使用%LET Statement、CALL SYMPUT、INTO Clause任意一种方式给创建的全局宏变量赋值。
示例程序一:
%macrocreate_global1;
*Use %GLOBAL Statement to create global macro variables.;
%globalgl_var4 gl_var5 gl_var6;
*Use %LET statement to assign value.;
%letgl_var4=global macro variable4;
*Use CALL SYMPUT to assign value.;
data _null_;
call symput('gl_var5', 'global macro variable5');
run;
*Use INTO Clause to assign value.;
proc sql noprint;
select count(*) into: gl_var6
from sashelp.class;
quit;
%mendcreate_global1;
%create_global1
%putgl_var4=gl_var4;
%putgl_var5=gl_var5;
%putgl_var6=gl_var6;
日志文件:
在SASHELP.VMACRO视图文件中同样可以看到这三个全局宏变量的信息。
比较特殊的情况
在宏程序内部,除了使用%GLOBAL语句创建。在某些情况下,还可以直接使用DATA步中的CALL SYMPUT创建。
在一个宏程序中,在包含CALL SYMPUT的DATA步程序之前,如果没有局部宏变量存在(即SAS系统还没有为该宏创建Local Symbol Table),那么该CALL SYMPUT创建的宏变量为全局的宏变量。
示例程序一:
%macrocreate_global2;
data _null_;
call symput('gl_var7', 'global macro variable7');
run;
%mendcreate_global2;
%create_global2
%putgl_var7=gl_var7;
日志文件:
同样该宏变量也存在于SASHELP.VMACRO视图文件中。
在上面的create_global2宏程序中,除DATA步之外,没有任何的宏变量,不存在Local Symbol Table;CALL SYMPUT创建的宏变量会存储在Global Symbol Table中(注:%LET语句和INTO子句无此功能)。
如果该宏程序是带参数的宏程序,或者在DATA步之前使用%LOCAL语句(或者%LET语句,或者INTO子句)创建了局部宏变量,那么SAS系统会为该宏程序创建一个Local Symbol Table;所以CALL SYMPUT创建的宏变量会存储在Local Symbol Table中,也就是会变成局部宏变量。
示例程序二:
*A macro with parameter;
%macrotest_global1(name);
data _null_;
call symput('gl_var8','global macro variable8');
run;
%putinside macro: name=name;
%putinside macro: gl_var8=gl_var8;
%mendtest_global1;
%test_global1
%putoutside macro: gl_var8=gl_var8;
*A macro with lcoal macro variable;
%macrotest_global2;
%letlocal_var1=local macro variable1;
data _null_;
call symput('gl_var9','global macro variable9');
run;
%putinside macro: local_var1=local_var1;
%putinside macro: gl_var9=gl_var9;
%mendtest_global2;
%test_global2
%putoutside macro: gl_var9=gl_var9;
日志文件:
细心的童鞋可能注意到上文中强调:在包含CALL SYMPUT的DATA步之前若没有Local Symbol Table,那么创建的宏变量就会存储在Global Symbol Table中,成为全局宏变量。所以,即使在DATA步之后有了局部的宏变量(有了Local Symbol Table),CALL SYMPUT创建的宏变量仍然是全局的。
示例程序三:
%macrocreate_global3;
data _null_;
call symput('gl_var10','global macro variable10');
run;
%letlocal_var1=local variable1;
%putinside macro: local_var1=local_var1;
%putinside macro: gl_var10=gl_var10;
%mendcreate_global3;
%create_global3
%putoutside macro: gl_var10=gl_var10;
因此,CALL SYMPUT创建的宏变量究竟是全局的,还是局部的,要看该DATA步所在的环境。如果在该DATA步执行时,无Local Symbol Table,那么创建的宏变量就为全局的;反之,就是局部的宏变量。
特别奇葩的情况
CALL SYMPUT除以上情况下可创建全局宏变量外,还有一种比较奇葩的情况。即使在DATA步之前有Local Symbol Table,CALL SYMPUT创建的宏变量仍旧是全局的。具体请看下面的示例程序:
示例程序:
%macrocreate_global4;
%letlocal_var=local macro variable;
data _null_;
call symput('gl_var11', 'global macro variable11');
%putinside macro: local_var=local_var;
%putinside macro: gl_var11=gl_var11;
%mendcreate_global4;
%create_global4
run;
%putoutside macro: local_var=local_var;
%putoutside macro: gl_var11=gl_var11;
日志文件:
在CREATE_GLOBAL4宏程序中的DATA步是不完整的,没有RUN语句。导致在宏运行后,DATA步并没有执行(因此没有创建宏变量GL_VAR11,这也是为什么在CREATE_GLOBAL4宏内部无发输出GL_VAR11的值)。
紧接着,在后面的OPEN CODE中遇到RUN语句,然后才执行该DATA步,实际上该DATA步是在OPEN CODE的环境中运行的。因此,创建的宏变量是全局的宏变量。
总结一下
总结来看,创建全局的宏变量有这么几种方式:
下面两个流程图是宏变量创建或赋值时、使用解析值时最基本的流程(注:并不适用于所有的情况,只是作为最基本流程的了解)。
请求创建或赋值给宏变量时的基本流程
使用宏变量,解析其取值时的基本流程
本期到这里就结束了,主要针对宏变量的Scope进行了说明,如何创建局部宏变量,如何创建全局宏变量。其核心就是Global Symbol Table和Local Symbol Table,只要搞清楚这二者以及上面的两个执行流程。那么整个Scope就会很清晰。更多内容,请持续关注SAS中文论坛——“SAS岩论-Macro系列”。
作者:辛岩,从事了多年的SAS数据分析挖掘工作,担任过项目经理、技术顾问、培训讲师等职务,拥有丰富的项目实战经验。
剑指SAS,尽在今朝。欢迎各位技术达人交流,可以长按下面群二维码入SAS中文论坛微信群@Slash,也可以发邮件:slash.xin@hotmail.com。
更多SAS岩论系列文章
SAS岩论 | Macro系列(10)——宏系统选项及其他
SAS岩论 | Macro系列(9)——Macro Function
SAS岩论 | Macro系列(8)——Macro Statement(2)
SAS岩论 | Macro系列(7)——Macro Statement(1)
SAS岩论 | Macro系列(6)——Macro Program
SAS岩论 | Macro系列(5)——Macro Variables(2)
SAS岩论 | Macro系列(4)——Macro Variables(1)
SAS岩论 | Macro系列(3)——运行机制
SAS岩论 | Macro系列(2)——总述篇
SAS岩论 | Macro系列(1)——入门篇
SAS岩论 | 使用DATA步并行处理超大数据集
SAS岩论|在SAS中如何解决中文乱码问题(1)
SAS岩论|在SAS中如何解决中文乱码问题(2)-干货篇
SAS岩论|SAS EG中如何显示Data步处理进度