【问题标题】:SAS macro quoting: pass equals sign as macro argumentSAS 宏引用:将等号作为宏参数传递
【发布时间】:2015-11-24 20:43:18
【问题描述】:

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

我很难引用具有相等“=”字符的参数。

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

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

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

我已阅读此 (http://www2.sas.com/proceedings/sugi28/011-28.pdf) 和其他 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;

【问题讨论】:

  • 作为旁注...没有必要宏引用等号。 SAS 足够聪明,知道等号只是参数值的一部分。您必须引用逗号,因为它们是宏参数的分隔符。

标签: sas sas-macro


【解决方案1】:

如果您按以下方式删除%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 */

所以它应该在有或没有参数的情况下工作。

【讨论】:

    【解决方案2】:

    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)也适用于这种情况,尽管他解释了一些可能有困难的情况。

    【讨论】:

      【解决方案3】:

      啊,这实际上是您遇到的一个棘手的小问题。该问题实际上是由对 %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(&param));
      

      注意:Amir 完全删除%if 语句的解决方案也是设计这样的代码的最佳方式。我只是提供更多详细信息来说明您遇到此问题的原因。


      补充说明 1 - 为什么不需要 %left()%trim

      顶部代码 sn-p 提供与具有 "%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() 函数在返回结果之前删除了任何宏引用。有点像这样:

      %put %unquote(%trim(&param));
      

      这可以通过再次使用 %str() 重新引用返回的结果来规避:

      %put %left(%str(%trim(&param)));
      

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

      %let param = %str(     inobs = 2        );
      %put %left(%trim(%nrstr(&param)));
      

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

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

      【讨论】:

      • 我想我基本上同意你在这里所说的,所以 +1 ...但我希望添加到这里的是两件事:一,添加他的第二阶段需要在这里使用%left(在%trim 周围使用第二个%str)和两个,可能是关于为什么它们也不需要它们的原因......
      • @Joe 好的。这有点乱,但谈论宏引用有点像打开潘多拉的盒子。
      • 为什么投反对票?这个正确答案“没用”怎么办? =/
      猜你喜欢
      • 1970-01-01
      • 2017-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多