漫谈SAS Macro (3)

您所在的位置:网站首页 sas中libname 漫谈SAS Macro (3)

漫谈SAS Macro (3)

#漫谈SAS Macro (3)| 来源: 网络整理| 查看: 265

所谓的Macro Quoting,就是%STR, %NRSTR, %NRBQUOTE, %BQUOTE一类的Macro Function。介绍这类函数一个非常常见的例子是,假定想用%LET语句为宏变量a赋值1;2:

%let a = 1;2; %put &a; ============================================================================ %let a = 1; 2; - 180 ERROR 180-322: Statement is not valid or it is used out of proper order. %put &a; 1

ERROR产生的原因其实显而易见: 如果不显式的指出1与2之间的分号是值的一部分的话,它就会被SAS认为是%LET语句的结束标志,于是&a的值是1。而分号之后的2;则被认为是非法语句。 这里面分号是一个特殊字符,当它作为宏变量的值的一部分存在时,可能会影响SAS编译一段代码的方式。

%let a = %str(1;2); %put &a; ================ 1;2

加上%STR之后就能顺利运行。类似的特殊字符有很多:

+-*/=^|~,;()&%'"

除了这些特殊字符以外,还有一类被称为mnemonics的字符串,包括

blank AND OR NOT EQ NE LE LT GE GT IN

这些mnemonics有时候也会造成意想不到的麻烦,举例如下:

%let a = a and b; %put &a; %let b = %eval(&a ne); ================================================================================ a and b ERROR: A character operand was found in the %EVAL function or %IF condition where a numierc operand is required. The condition was: a and b ne

这里面的错误源于SAS在evaluate括号里面的值时,遇到and会将之认为是“逻辑与 ”,从而a逻辑与b的值,进而引发错误。需要指出的是,将&a ne 写到%IF语句中会引发一样的错误。

1.Macro Quoting的原理

Quoting function在作用时,先把值的两端各加上一个特殊的字节,叫做delta character,用来标识mask的开始和结束。不同的quoting function使用不同的开始和结束标识。

%STR: x01 x02 %NRSTR: x01 x02 %BQUOTE: x04 x08 %NRBQUTOE: x06 x08

在值的内部,特殊字符则被替换成其他他字符,这里只看一个例子:

%let a = %str(a+b); data _null_; set sashelp.vmacro; if name = 'A' then put value $hex10.; run; ================================================================================ 0161156202

可以看到+(x2B)被替换成了x15。如果感兴趣可以是使用类似的program将所有的特殊字符的替代字符打印出来。

2. Quoting function的类型

可以依照作用时间将上述quoting function分成两类:compliation function和execution function。前者的mask作用发生在编译期间,后者发生在运行期间。关于编译和运行的概念,这里不深入讨论了,仅提供一个例子:

%macro getMaxVal(indata, invar); /* returns maxium value of a variable from a dataset*/ %if %symexist(_max_) %then %let _max_ =; %global _max_; proc sql noprint; select max(&invar) into: _max_ from &indata; quit; %mend getMaxval; data class; length sexl $10 sexn 8; set sashelp.class; call missing(sexl, sexn); run; data notnull; length max $200; set sashelp.vcolumn; where libname = 'WORK' and memname='CLASS'; call execute('%getMaxVal('||strip(memname)||', '||strip(name)||')'); max = strip("&_max_"); if type ='char' and max = " " then delete; else if type = 'num' and max = "." then delete; keep memname name type max; run;

在上面的例子中首先定义了一个宏%getMaxVal,从给定的data中返回某变量的最大值。然后基于SASHELP.CLASS构造了一个测试数据集,包含一个数值型空变量和字符型空变量。结尾的data步的思路是循环检查测试数据集中各变量的最大值,如果最大值是空值则删掉该变量所代表的观测。然而,当你首次运行这个macro的时候,首先会发现log中的warning,注意这个warning的位置:

18 data notnull; 19 length max $200; 20 set sashelp.vcolumn; 21 where libname = 'WORK' and memname='CLASS'; 22 call execute('%getMaxVal('||strip(memname)||', '||strip(name)||')'); 23 max = strip("&_max_"); WARNING: Apparent symbolic reference _MAX_ not resolved. 24 if type ='char' and max = " " then delete; 25 else if type = 'num' and max = "." then delete; 26 keep memname name type max; 27 run;

如果你打开运行后的数据集notnull会发现,变量max在所有的观测上的值都是&_max_,也就是说这个宏变量没有解析。为什么呢?因为当你submit这段程序时,程序首先要被编译。在编译期间,SAS遇到

max = strip("&_max_");

时会从全局宏变量中查找宏变量_max_。这时这个变量还不存在,因为

call execute('%getMaxVal('||strip(memname)||', '||strip(name)||')');

要等到程序运行期间才能真正执行,宏变量_max_才能由宏 %getMaxVal创建。如果你尝试再次运行该段程序,会发现这个warning不见了,而打开数据集notnull,则发现变量max在每个观测上面的值均是150。多次运行的结果均是如此,至于原因就留给大家自己思考了。

%STR和%NRSTR的作用发生在编译期间,%BQUOTE和%NRBQUOTE的作用发生在运行期间。这里总结几点这几个函数在用法上面的异同:

1) %NRSTR相比%STR,会额外屏蔽字符&和字符%。%NRBQUOTE和%BQUOTE类似。函数名开头的NR表示Not Resolved。

2) %STR和%BQUOTE除了作用时间的差异以外,%BQUOTE函数会忽略不成对的括号,单引号和双引号。而%STR函数不具有这个功能,如果其作用的字符串中有不成对的括号,单引号或者双引号,则需要在该字符前加上%号。比如:

%let a = %str(a%'b); ** a % must be added to unmatched single quotation mark; %let b = %bquote(a'b); %put %NRSTR(&a): &a; %put %NRSTR(&b): &b; =============================================================================== a: a'b b: a'b

3) 因为%STR的作用发生在编译期间,所以它往往用来屏蔽一个常字符串(constant string)。类似于%str(&a)的写法其实没有太大的意义:

%macro test(val); %let a= %str(&val); data _null_; set sashelp.vmacro; if name = 'A' then put value $hex10.; run; %mend test; %test(a+b); ================================================================================ 01412B4202

和上面的例子比较,可以看到,+并没有被替换成字符x15。这是因为%STR的作用发生在编译期间,macro test里面的%let语句在编译时相当于%let a = x01&valx02。 此时&val的值还未被解析(因为编译的时候宏参数val的值还未定)。等到macro运行期间,参数的值确定,于是%let a = x01a+bx02。这样就造成了上面的结果。实际上这个例子的结果非常重要,考虑下面一个更加实际的macro:

%macro isNull(val); %* check if input value is null; %if %nrbquote(&val) ne %then FALSE; %else TRUE; %mend isNull; %put %isNull(a+b); ** correct; ================================================================================ FALSE %macro isNull2(val); %* check if input value is null; %if %str(&val) ne %then FALSE; %else TRUE; %mend isNull2; %put %isNull2(a+b); ** incorrect. syntax error; ================================================================================ ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was: &val ne ERROR: The macro ISNULL2 will stop executing.

因为val可能会解析成任何值,所以%STR不是一个robust的选择。

4) 因为%NRSTR是发生在编译期间,而且会屏蔽%和&,所以适合用来屏蔽一串可能含有%或&的长字符串,并且字符串中&和%并非宏变量或者%的trigger。

data report(label="%nrstr(%change from baseline)"); /* more steps */ run;

5)由于 %BQUOTE和%NRBQUOTE的作用发生在运行期间,所以括号内的宏变量或者宏会尽可能的解析。

data _null_; call symput('client', 'Johnson&Johnson'); run; %let fullstr = %bquote(Client: &client); %let fullstr2 = %nrbquote(Client: &client); ================================================================================ 6 data _null_; 7 call symput('client', 'Johnson&Johnson'); 8 run; NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.01 seconds 11 %let fullstr = %bquote(Client: &client); WARNING: Apparent symbolic reference JOHNSON not resolved. WARNING: Apparent symbolic reference JOHNSON not resolved. 13 %let fullstr2 = %nrbquote(Client: &client); WARNING: Apparent symbolic reference JOHNSON not resolved.

先讲讲为什么fullstr2下面会有一个warning。当

%let fullstr2 = %nrbquote(Client: &client);

被编译时,&client被解析成Johnson&Johnson.&Johnson触发了编译器寻找宏变量johnson,而这个宏变量实际上不存在,于是编译器发出一个warning。在执行阶段,由于%NRBQUOTE屏蔽字符&,不再有新的warning产生。这也顺便解释了为什么第一个%let语句下面有两个warning。我们用sashelp.vmacro来检验我们的理论:

data _null_; set sashelp.vmacro; if name='FULLSTR' then put ' FULLSTR: ' value $hex50.; if name='FULLSTR2' then put 'FULLSTR2: ' value $hex50.; run; ================================================================================ FULLSTR: 04436C69656E743A204A6F686E736F6E 26 4A6F686E736F6E08 FULLSTR2: 06436C69656E743A204A6F686E736F6E 0F 4A6F686E736F6E08

注意输出结果中用空格分开的部分,FULLSTR中&没有被mask,而FULLSTR2中&则被替换成了x0F.

==================================分割线=============================

最近一段时间稍微有点忙,而且又有点犯懒,而实际写本节时这些例子也想了很久,故而好久未更新。限于篇幅,本节还有一半的内容下次再更。到时再见。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3