宏似乎陷入无限循环,不知道如何调试

macro seems stuck in infinite loop, don't know how to debug

我正在尝试定义一个宏函数,该函数 returns 是 space 分隔列表中的唯一列表项。这个宏本身使用了我测试过的其他宏,它们似乎自己工作得很好(见下面的例子),都是非常简单的代码。

但是由于某种原因,代码无限期运行,我不知道如何正确调试它。我通常使用 %put 语句进行调试,但它们不会在此处打印,因为没有错误,我手动停止了代码。

这是主要的宏,后面是我使用的其他方便的宏,您可以执行整个批处理以加载宏,然后查看给定的示例。

*---------------------------------------------------------------;
* LIST_UNIQUE                                                   ;
* Return only unique items from list,                           ;
* in order of first appearance                                  ;
*---------------------------------------------------------------;
/* EXAMPLE
%put %list_unique();    ** (nothing)
%put %list_unique(a);   ** a
%put %list_unique(a a); ** doesn't work (should be a)
%put %list_unique(a b); ** doesn't work (should be a b)
*/
%macro list_unique(data);
%local out curr_item;
%do i=1 %to %list_length(&data);
   %let curr_item = %extract(&data,&i);
   %if not %list_in(&curr_item,&out) %then %let out = &out &curr_item;
%end;
&out
%mend;

*---------------------------------------------------------------;
* LIST_LENGTH                                                   ;
* Length of space separated list                                ;
*---------------------------------------------------------------;
/* EXAMPLES :   
   %put %list_length(); ** 0
   %put %list_length(item1 item2 item3); ** 3
*/
%macro list_length(data);
%sysfunc(countw(&data,%str( )))
%mend;

*---------------------------------------------------------------;
* LIST_IN                                                       ;
* check if item is in list                                      ;
*---------------------------------------------------------------;
/* EXAMPLE
%put %list_in(,a);        ** 0
%put %list_in(a,);        ** 0
%put %list_in(a,a);       ** 1
%put %list_in(a,a a);     ** 1
%put %list_in(b,a b c d); ** 1
%put %list_in(e,a b c d); ** 0
*/
%macro list_in
(item /* item to search in list */
,list /* space separated list to quote */
);
/* exception when list has null length */
%if not %length(&list) %then 0%return; 
/* general case */
%do i=1 %to %list_length(&list);
   %if %extract_pos(&list,&i) = &item %then 1%return;
%end;
0
%mend;

*-------------------------------------------------------------------------------;
* EXTRACT_POS                                                                   ;
* Extracts subset of values from space separated list                           ;
*-------------------------------------------------------------------------------;
/* EXAMPLES
%put %extract_pos(,1);        ** (nothing)
%put %extract_pos(a b c d,);  ** (nothing)
%put %extract_pos(a b c d,1); ** a
%put %extract_pos(a b c d,2 1:3 1); ** b a b c a
*/
%macro extract_pos
(data
,ind
);
%local i j token output new_ind;
%do i=1 %to %sysfunc(countw(&ind,%str( )));
  %let token = %scan(&ind,&i,%str( ));
  %if %index(&token,:) %then %do; /* if token with ':' */
    %do j=%scan(&token,1,:) %to %scan(&token,2,:);
      %let output = &output %scan(&data,&j,%str( ));
    %end;
  %end;
  %else %do;                      /* if simple token */
      %let output = &output %scan(&data,&token,%str( ));
  %end;
%end;
&output
%mend;
  1. 通过将此行添加到代码的开头来启用宏调试,这将解析宏代码和变量:

    Options macrogen symbolgen mlogic mprint mfile;
    
  2. 运行 您的代码并查看您的日志以获取详细信息,

  3. 完成后,通过将步骤 1 中的选项替换为以下选项来禁用宏调试:

    Options nomacrogen NoSymbolgen nomlogic nomprint nomfile;
    

有关更多详细信息,您可以查看 SAS 调试文档 http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#a001066200.htm

变量 I 在名称空间中向下共享。一个简单的解决方法是在每个宏中使用不同的循环变量。

找到一些关于 SAS 共享逻辑的文档。 SAS Blogs

您无法保护您调用的宏免于修改您的宏变量,但如果宏设计得当,它们就不会。除非您打算修改任何现有的宏变量,否则您需要将宏变量定义为局部变量。您的一个或多个宏正在使用宏变量 I 而未将其定义为本地。因此,如果已经存在一个名为 I 的宏变量,那么该宏会修改现有变量的值。

您的一个宏也在调用 %extract() 而不是 %extract_pos()

我还简化了您的 %list_in() 宏,使其成为对现有 SAS 函数的调用,例如您的 %list_length() 宏。

%macro list_unique
/*---------------------------------------------------------------
Return only unique items from list
---------------------------------------------------------------*/
(data  /* Space delimited list of items */
);
%local i curr_item out ;
%do i=1 %to %list_length(&data);
  %let curr_item = %extract_pos(&data,&i);
  %if not %list_in(&curr_item,&out) %then %let out=&out &curr_item;
%end;
&out
%mend list_unique;

%macro list_length(data);
%sysfunc(countw(&data,%str( )))
%mend list_length;

%macro list_in
/*---------------------------------------------------------------
Check if item is in list
---------------------------------------------------------------*/
(item /* item to find in list */
,list /* space separated list to search */
);
%sysevalf(%sysfunc(indexw(&list,&item,%str( ))),boolean)
%mend list_in;

%macro extract_pos
/*-------------------------------------------------------------------------------
Extracts subset of values from space separated list
-------------------------------------------------------------------------------*/
(data   /* Space delimited list of values */
,ind    /* Space delimited list of positions or position ranges */
);
%local i j token output;
%do i=1 %to %sysfunc(countw(&ind,%str( )));
  %let token = %scan(&ind,&i,%str( ));
  %do j=%scan(&token,1,:) %to %scan(&token,-1,:);
/* Token is single position or range in format start:end */
    %let output = &output %scan(&data,&j,%str( ));
  %end;
%end;
&output
%mend extract_pos;

测试

831  %put %list_unique();    %** (nothing);

832  %put %list_unique(a);   %** a ;
a
833  %put %list_unique(a a); %** doesnot work (should be a);
a
834  %put %list_unique(a b); %** doesnot work (should be a b);
a b