先讲一个宏嵌套需要注意的地方:
就是不要把宏定义放在另一个宏中,否则的话你每次调用宏的时候,宏嵌套里面的宏定义都会被编译一次。但是并不需要这样,宏定义被编译一次就够了。
下面这段程序程序就是每次调用宏ABC的时候,宏def都会被编译一次。
%macro abc;
XXXX;
%macro def;
XXXX;
%mend def;
%def
XXXX
%mend abc ;
%abc
/************************/
下面这段程序程序就是每次调用的时候,宏def只会被编译一次,只要保证在d调用宏abc之前宏def编译完成既可以了。
/*********************/
%macro abc;
XXXX;
%def
XXXX
%mend abc ;
%macro def;
XXXX;
%mend def;
%abc
/********************/
今天讲讲宏里面的条件执行语句%if %then和 %else
宏里面的%if %then和 %else 跟data步里面的if then else 很相似,但是%if %then %else不限制于data步,且不对data步中的变量进行操作。
%macro tets;
data test2;
a=20;
b=9.1;
%if a>b %then flag="Y"; /*对data步中的变量直接进行操作是无用的*/
run;
%mend;
%tets;
%if %then和 %else 可能出现在宏里面的任何地方,并且在一定条件下可以实现下面这些功能:
1)赋值给宏变量
2)执行其他宏
3)创建SAS代码和SAS语句
但是在写%if %then和 %else 的时候,需要注意以下几个地方:
1)它们不对data步中的变量进行操作,因为宏语句和宏定义都是在data步变量读取 之后就开始编译的!(记住这个和宏变量的解析顺序我觉得同等重要)宏表达式永远不会直接计算PDV上变量的值。
2)它们控制程序流程和代码构造,而不是DATA步骤的执行流程。
3)对文本字符串进行比较,通常不使用引号,也不需要引号。比如“X”=‘X’并不相等。
%if &city=Albany %then %let state=New York;
%else %let state=California;
在这段程序中,Albany并没有用引号括起来,在data步中,假设var="twst",通过这段程序,SAS就知道VAR是变量,“twst”是VAR变量的值,并且VAR是字符型变量,但是在宏中,宏变量的名称前总是有一个&,宏变量很容易与文本区分开来,因此,我们并不需要用引号把Albany括起来。
在data步中,我们需要注意变量的长度,但是在宏语言中,存储是根据需要动态分配的,因此截断不是问题。所以在上面的例子中,即使New York比California长度短,并且先输出New York,也不会造成截断问题,但是在data步中SAS是根据第一个输入的变量的值确定长度的(如果没有提前定义变量长度的话)。
%macro precomp(sexmain=,obs=);
data test;
set sashelp.class(obs=&obs);
if sex=&sexmain;
run;
/*调用precomp里的宏参数*/
%locate(sex=&sexmain,no=&obs)
data test2;
set sashelp.cars;
run;
%ccmaro
%mend precomp;
%macro locate(sex=, no=);
data test3;
set sashelp.class(obs=&no);
if sex=&sex;
run;
%mend locate;
%macro ccmaro;
data test4;
set sashelp.cars;
if _n_=7;
run;
%mend ccmaro;
%precomp(sexmain="F",obs=7);
%macro condido(indata=,sex=,no=10);
%if &sex ne %then %locate(sex=&sex, no=&no);
%else %put macro locate was no called;
%ccmaro
%mend;
%macro condido(indata=,sex=,no=10);
%if &sex ne %then %locate(sex=&sex, no=&no);
%else %put macro locate was no called;
%ccmaro
%mend;
注意上面这段程序的写法,在ne后面并没有等于号,这在data步中肯定是会报错的,但是在宏语言中,宏变量可以包含一个null value(注意这跟空白是不同的),而且因为引号并不是必须的,所以这种写法是可以正确执行的而且看起来有点逼格。
还有一种有逼格的写法:
%let ccc=var;
%put &=ccc;
这段程序会输出成CCC=var,如果我们只是%put &ccc,log的输出结果只会有一个var
如果你想为了避免歧义,可以把这段程序完善一下
%if "&sex" ne "" %then %locate(sex=&sex, no=&no);
但是这样写并不推荐,一是这样写并没有完全理解%if的执行机制,另一个就是如果双引号之间有一个空格,那么这个空格也会被参与比较。当引号被包括在内时,它们就成为要比较的文本的一部分;因此,不能在右侧使用单引号,在左侧使用双引号。
通过%if语句还能够创建在实际执行之前没有完全定义的程序,顺便讲讲宏中分号的使用。
%macro dyna(num=);
data dydat;
set
%if &num=3 %then sashelp.class; /*①*/
%else sashelp.cars; /*②*/
; /*③*/
run;
%mend;
通过上面这段程序,通过%if我们能灵活控制程序,读取对应的数据集。需要注意的是,初学者在位置①②③可能会犯错。
首先要知道%if 也要用分号告诉SAS我要结束一段语句了,所以位置①的分号是%if %then语句的结束,位置②是%else语句的结束;位置③是set语句的结束。
%if &num=3 %then sashelp.class;; /①/
%else sashelp.cars;; /②/
如果我在位置①②分别再加上分号,这时候SAS会报错。
原因是:
位置①:第一个分号是跟%if合在一起的,用来关闭%if语句,然后作为宏语言先一起编译;这时候第二个分号就不属于宏的一部分了,但是宏scanner知道当%else跟在%if 后面的时候,%else语句属于紧接着的第二段语句,但是你多加了一个分号,相当于把这个过程打断了。
所以当宏scanner扫描到%else的时候,它没有找到你的%if语句,这时候就报错了!所以会报红框中的错误。所以按照这个思路,如果只是在位置②多加一个分号,程序是能够正确执行的。
data df;
set sashelp.class;
if sex="F" then flag="Y";;
else flag="N";
run;
这跟在data步是一样的逻辑的,SAS也会报没有匹配的if then语句。
网友评论