SAS宏引用:传递等号作为宏参数

SAS宏引用:传递等号作为宏参数,sas,sas-macro,Sas,Sas Macro,我正在编写一个宏,它在某个时候调用一些proc-SQL代码。我希望用户能够指定任意proc sql选项(例如,inobs=100可以是我的宏的输入参数之一) 我很难引用一个具有等号“=”字符的论点 其中一个问题是,我还应该检查宏参数是否为空,如果它不是空的,只有在sql语句中添加指定的选项 下面是一个示例非工作测试,它不工作并抛出 错误:未使用宏定义关键字参数INOBS 我读过这篇()和其他SUGI的文章,并尝试了许多引用和调用宏的方法 如果有人能提供以下功能的工作示例,将不胜感激 option

我正在编写一个宏,它在某个时候调用一些proc-SQL代码。我希望用户能够指定任意proc sql选项(例如,inobs=100可以是我的宏的输入参数之一)

我很难引用一个具有等号“=”字符的论点

其中一个问题是,我还应该检查宏参数是否为空,如果它不是空的,只有在sql语句中添加指定的选项

下面是一个示例非工作测试,它不工作并抛出

错误:未使用宏定义关键字参数INOBS

我读过这篇()和其他SUGI的文章,并尝试了许多引用和调用宏的方法

如果有人能提供以下功能的工作示例,将不胜感激

options mprint mlogic;

data have;
    length x $8;
    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()
引起的

删除这些会导致代码按预期工作(注意,我还删除了参数周围的宏引号):

我们可以重新创建您遇到的问题,如下所示:

%put %trim(inobs=1);
因为参数解析为inobs=1,并且
%trim()
没有任何命名参数,所以它抛出了一个嘶嘶的拟合。要正确传入包含“inobs=1”的字符串,我们可以这样做:

%let param = inobs=1;
%put %trim(%str(&param));
%put %unquote(%trim(&param));
注意:Amir的解决方案是删除
%if
语句,这也是设计类似代码的最佳方法。我只是想提供更多的细节,说明你为什么会有这个问题


附加解释1-为什么不需要
%left()
%trim
顶部的代码段提供了与原始代码相同的预期功能,原始代码具有
“%left(%trim(&sqlOptions.))”
。这是因为除非使用宏引号显式保留开头和结尾空格,否则会从宏变量(包括宏参数)中删除开头和结尾空格。一个简单的例子可以说明这一点:

%let param =      lots      of     spaces        ;
%put ***&param***;
给出:

***lots      of     spaces***
您可以看到,内部空白被保留,但左右填充消失了。要保留空白,只需使用
%str()
函数即可

%let param = %str(     lots      of     spaces        );
%put ***&param***;
给出:

***     lots      of     spaces        ***

附加说明2-使用包含空格的宏 如果您确实在宏变量上有空格,您需要删除它,因为它被引用了,并且您想使用
%left()
%trim()
来执行此操作,那么事情会变得有点奇怪。我们的变量可以这样创建:

%let param = %str(     inobs = 2        );
您可以看到,为了创建它,我们已经用%str()引用了该值。这意味着我们现在可以调用其中一个函数,而无需再次引用:

%put %trim(&param);  * ALREADY QUOTED AT CREATION SO THIS WORKS FINE;
但是,如果我们尝试将结果输入到
%left()
函数中,我们又回到了原来的问题:

%put %left(%trim(&param));  * OOPS. DOESNT WORK;
现在我在这里猜测,但我相信这很可能是因为
%trim()
函数在返回结果之前删除了任何宏引用。有点像这样:

%let param = inobs=1;
%put %trim(%str(&param));
%put %unquote(%trim(&param));
可以通过再次使用%str()重新引用返回的结果来避免此问题:

。。。或使用%nrstr()包装原始参数:

。。。或者使用
%sysfunc()
调用datastep函数:

%put %sysfunc(compress(&param));

阿米尔的解决方案可能适合您的特定用例。但要回答更一般的问题,我们需要看一看关于宏观参数测试的开创性论文,张忠的

他的示例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(仅使用SUPERQ而不使用SYSEVALF)也适用于这种情况,尽管他解释了一些可能有困难的情况。

作为旁注。。。不需要宏引用等号。SAS非常聪明,知道等号只是参数值的一部分。你必须引用逗号,因为这些是宏参数的定界。我想我大体上同意你在这里所说的,所以+1。。。但我希望添加两件事:一,在这里添加他需要使用的第二个阶段(第二个
%str
%trim
周围)和第二个,可能是关于为什么不需要它们的原因…@Joe Ok done。这有点混乱,但谈论宏观报价有点像打开潘多拉的盒子。为什么要投反对票?这个正确答案“没用”怎么说=/
%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);