SAS 宏引用:将等号作为宏参数传递
SAS macro quoting: pass equals sign as macro argument
我正在编写一个宏,该宏有时会调用一些 proc SQL 代码。我希望用户能够指定任意 proc sql 选项(例如 inobs=100 可能是我的宏的输入参数之一)。
我很难引用具有相等“=”字符的参数。
其中一个问题是我还应该检查宏参数是否为空,如果不为空,才将指定的选项添加到 sql 语句中。
下面是一个无效测试示例,它不起作用并抛出
ERROR: The keyword parameter INOBS was not defined with the macro.
我已阅读此 (http://www2.sas.com/proceedings/sugi28/011-28.pdf) 和其他 SUGI 的内容,并尝试了许多可能的方法来引用和调用宏。
如果有人可以提供以下功能的工作示例,将不胜感激。
options mprint mlogic;
data have;
length x ;
input x;
datalines;
one
two
three
;
proc sql inobs=2;
create table sql_output as
select *
from have;
quit;
%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
proc sql
%if "%left(%trim(&sqlOptions.))" ne "" %then %do;
&sqlOptions.
%end;
/* the semicolon to end the proc sql statement */
;
create table macro_output as
select *
from have;
quit;
%mend;
%pass_parameter_with_equal_sign(table=have, sqlOptions=%str(inobs=2))
title "SQL output:";
proc print data=sql_output; run;
title "Macro output:";
proc print data=macro_output; run;
如果按如下方式删除 %if
条件,它应该会起作用:
%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
proc sql
&sqlOptions.
/* the semicolon to end the proc sql statement */
;
create table macro_output as
select *
from have;
quit;
%mend;
您使用的%if
是检查&sqlOptions
是否为空,如果您按原样使用它应该无关紧要,因为它的无条件使用会给出:
proc sql inobs=2; /* in the case of &sqlOptions=inobs=2 */
或者如果没有为 &sqlOptions
提供值,那么您应该看到:
proc sql; /* i.e. no options specified */
所以不管有没有参数,它都应该工作。
啊,这实际上是您 运行 遇到的一个棘手的小问题。该问题实际上是由对 %trim()
和 %left()
.
的调用引起的
在代码中删除这些结果会按预期工作(请注意,我还删除了参数周围的宏引号):
%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
proc sql
%if "&sqlOptions" ne "" %then %do;
&sqlOptions
%end;
/* the semicolon to end the proc sql statement */
;
create table macro_output as
select *
from &table;
quit;
%mend;
%pass_parameter_with_equal_sign(table=sashelp.class, sqlOptions= inobs=2);
我们可以像这样重现您遇到的问题:
%put %trim(inobs=1);
因为参数解析为 inobs=1,而 %trim()
没有任何命名参数,所以发出嘶嘶声。要正确传递包含 "inobs=1" 的字符串,我们可以这样做:
%let param = inobs=1;
%put %trim(%str(¶m));
注意:Amir 完全删除 %if
语句的解决方案也是设计此类代码的最佳方式。我只是提供有关您遇到此问题的原因的更多详细信息。
补充说明 1 - 为什么不需要 %left()
和 %trim
顶部代码片段提供与具有 "%left(%trim(&sqlOptions.))"
的原始代码相同的预期功能。这是因为开头和结尾的空格从宏变量(包括宏参数)中删除,除非使用宏引用显式保留它。一个简单的例子来说明这一点:
%let param = lots of spaces ;
%put ***¶m***;
给出:
***lots of spaces***
可以看到保留了内部空白,但是左右填充没有了。要保留空格,我们可以简单地使用 %str()
函数。
%let param = %str( lots of spaces );
%put ***¶m***;
给出:
*** lots of spaces ***
补充说明 2 - 使用包含空格的宏
如果你确实在一个宏变量上有空格,你需要删除它,因为它被引用了,并且你想使用 %left()
和 %trim()
来这样做,那么事情就会变得有点古怪的。我们的变量可以这样创建:
%let param = %str( inobs = 2 );
您可以看到我们已经用 %str() 引用了该值以创建它。这意味着我们现在可以调用其中一个函数而无需再次引用它:
%put %trim(¶m); * ALREADY QUOTED AT CREATION SO THIS WORKS FINE;
但是,如果我们随后尝试将结果输入 %left()
函数,我们又回到了最初的问题:
%put %left(%trim(¶m)); * OOPS. DOESNT WORK;
现在我在这里猜测,但我相信这很可能是因为 %trim()
函数在返回结果之前删除了任何宏引用。有点像这样:
%put %unquote(%trim(¶m));
这可以通过再次使用 %str() 重新引用返回的结果来规避:
%put %left(%str(%trim(¶m)));
... 或用 %nrstr():
包装原始参数
%let param = %str( inobs = 2 );
%put %left(%trim(%nrstr(¶m)));
... 或使用 %sysfunc()
调用数据步函数:
%put %sysfunc(compress(¶m));
Amir 的解决方案可能适合您的特定用例。但要回答更普遍的问题,我们需要查看有关宏观参数测试的开创性论文,Chang Chung 的 Is This Macro Parameter Blank?.
他的示例 C8 在这里适合您,尽管其他一些也适用。
%if %sysevalf(%superq(param)=,boolean) %then ... /* C8 */
例如:
%macro test_me(param=);
%if %sysevalf(%superq(param)=,boolean) %then %put Empty;
%else %put Not Empty;;
%mend test_me;
%test_me(param=);
%test_me(param=MyParam);
%test_me(param=param=5);
%SUPERQ 在这里最有用,因为它避免解析宏参数。相反,它将它保留为一个宏参数值 - 完全未解析 - 并允许您以这种方式使用它;这样您就不会被讨厌的等号所困扰。
他的 C4(只使用没有 SYSEVALF 的 SUPERQ)在这种情况下也有效,尽管他解释了一些可能有困难的情况。
我正在编写一个宏,该宏有时会调用一些 proc SQL 代码。我希望用户能够指定任意 proc sql 选项(例如 inobs=100 可能是我的宏的输入参数之一)。
我很难引用具有相等“=”字符的参数。
其中一个问题是我还应该检查宏参数是否为空,如果不为空,才将指定的选项添加到 sql 语句中。
下面是一个无效测试示例,它不起作用并抛出
ERROR: The keyword parameter INOBS was not defined with the macro.
我已阅读此 (http://www2.sas.com/proceedings/sugi28/011-28.pdf) 和其他 SUGI 的内容,并尝试了许多可能的方法来引用和调用宏。
如果有人可以提供以下功能的工作示例,将不胜感激。
options mprint mlogic;
data have;
length x ;
input x;
datalines;
one
two
three
;
proc sql inobs=2;
create table sql_output as
select *
from have;
quit;
%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
proc sql
%if "%left(%trim(&sqlOptions.))" ne "" %then %do;
&sqlOptions.
%end;
/* the semicolon to end the proc sql statement */
;
create table macro_output as
select *
from have;
quit;
%mend;
%pass_parameter_with_equal_sign(table=have, sqlOptions=%str(inobs=2))
title "SQL output:";
proc print data=sql_output; run;
title "Macro output:";
proc print data=macro_output; run;
如果按如下方式删除 %if
条件,它应该会起作用:
%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
proc sql
&sqlOptions.
/* the semicolon to end the proc sql statement */
;
create table macro_output as
select *
from have;
quit;
%mend;
您使用的%if
是检查&sqlOptions
是否为空,如果您按原样使用它应该无关紧要,因为它的无条件使用会给出:
proc sql inobs=2; /* in the case of &sqlOptions=inobs=2 */
或者如果没有为 &sqlOptions
提供值,那么您应该看到:
proc sql; /* i.e. no options specified */
所以不管有没有参数,它都应该工作。
啊,这实际上是您 运行 遇到的一个棘手的小问题。该问题实际上是由对 %trim()
和 %left()
.
在代码中删除这些结果会按预期工作(请注意,我还删除了参数周围的宏引号):
%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
proc sql
%if "&sqlOptions" ne "" %then %do;
&sqlOptions
%end;
/* the semicolon to end the proc sql statement */
;
create table macro_output as
select *
from &table;
quit;
%mend;
%pass_parameter_with_equal_sign(table=sashelp.class, sqlOptions= inobs=2);
我们可以像这样重现您遇到的问题:
%put %trim(inobs=1);
因为参数解析为 inobs=1,而 %trim()
没有任何命名参数,所以发出嘶嘶声。要正确传递包含 "inobs=1" 的字符串,我们可以这样做:
%let param = inobs=1;
%put %trim(%str(¶m));
注意:Amir 完全删除 %if
语句的解决方案也是设计此类代码的最佳方式。我只是提供有关您遇到此问题的原因的更多详细信息。
补充说明 1 - 为什么不需要 %left()
和 %trim
顶部代码片段提供与具有 "%left(%trim(&sqlOptions.))"
的原始代码相同的预期功能。这是因为开头和结尾的空格从宏变量(包括宏参数)中删除,除非使用宏引用显式保留它。一个简单的例子来说明这一点:
%let param = lots of spaces ;
%put ***¶m***;
给出:
***lots of spaces***
可以看到保留了内部空白,但是左右填充没有了。要保留空格,我们可以简单地使用 %str()
函数。
%let param = %str( lots of spaces );
%put ***¶m***;
给出:
*** lots of spaces ***
补充说明 2 - 使用包含空格的宏
如果你确实在一个宏变量上有空格,你需要删除它,因为它被引用了,并且你想使用 %left()
和 %trim()
来这样做,那么事情就会变得有点古怪的。我们的变量可以这样创建:
%let param = %str( inobs = 2 );
您可以看到我们已经用 %str() 引用了该值以创建它。这意味着我们现在可以调用其中一个函数而无需再次引用它:
%put %trim(¶m); * ALREADY QUOTED AT CREATION SO THIS WORKS FINE;
但是,如果我们随后尝试将结果输入 %left()
函数,我们又回到了最初的问题:
%put %left(%trim(¶m)); * OOPS. DOESNT WORK;
现在我在这里猜测,但我相信这很可能是因为 %trim()
函数在返回结果之前删除了任何宏引用。有点像这样:
%put %unquote(%trim(¶m));
这可以通过再次使用 %str() 重新引用返回的结果来规避:
%put %left(%str(%trim(¶m)));
... 或用 %nrstr():
包装原始参数%let param = %str( inobs = 2 );
%put %left(%trim(%nrstr(¶m)));
... 或使用 %sysfunc()
调用数据步函数:
%put %sysfunc(compress(¶m));
Amir 的解决方案可能适合您的特定用例。但要回答更普遍的问题,我们需要查看有关宏观参数测试的开创性论文,Chang Chung 的 Is This Macro Parameter Blank?.
他的示例 C8 在这里适合您,尽管其他一些也适用。
%if %sysevalf(%superq(param)=,boolean) %then ... /* C8 */
例如:
%macro test_me(param=);
%if %sysevalf(%superq(param)=,boolean) %then %put Empty;
%else %put Not Empty;;
%mend test_me;
%test_me(param=);
%test_me(param=MyParam);
%test_me(param=param=5);
%SUPERQ 在这里最有用,因为它避免解析宏参数。相反,它将它保留为一个宏参数值 - 完全未解析 - 并允许您以这种方式使用它;这样您就不会被讨厌的等号所困扰。
他的 C4(只使用没有 SYSEVALF 的 SUPERQ)在这种情况下也有效,尽管他解释了一些可能有困难的情况。