Sas 宏似乎卡在无限循环中,don';我不知道如何调试

Sas 宏似乎卡在无限循环中,don';我不知道如何调试,sas,sas-macro,Sas,Sas Macro,我试图定义一个宏函数,它从一个空格分隔的列表中返回唯一的列表项。这个宏本身使用了我测试过的其他宏,它们自己似乎工作得很好(参见下面的示例),它们都是非常简单的代码 然而,由于某些原因,代码无限期地运行,我不知道如何正确地调试它。我通常使用%put语句进行调试,但它们不会在这里打印,因为没有错误,我手动停止代码 这里是主宏,后面是我使用的其他方便宏,您可以执行整个批来加载宏,然后查看给定的示例 *---------------------------------------------------

我试图定义一个宏函数,它从一个空格分隔的列表中返回唯一的列表项。这个宏本身使用了我测试过的其他宏,它们自己似乎工作得很好(参见下面的示例),它们都是非常简单的代码

然而,由于某些原因,代码无限期地运行,我不知道如何正确地调试它。我通常使用
%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;
  • 通过将此行添加到代码开头来启用宏调试,这将解析宏代码和变量:

    Options macrogen symbolgen mlogic mprint mfile;
    
  • 运行代码并查看日志以了解详细信息

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

    Options nomacrogen NoSymbolgen nomlogic nomprint nomfile;
    
  • 有关更多详细信息,您可以查看SAS调试文档

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


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

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

    还有一个宏正在调用
    %extract()
    ,而不是
    %extract\u pos()

    我还将
    %list\u in()
    宏简化为对现有SAS函数的调用,就像您的
    %list\u 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;
    
    试验


    可能变量
    I
    extract\u pos
    宏中的
    list\u之间共享?我会尝试使用特定于宏的循环变量名。很好!问题是我天真地假设在
    %do
    语句中创建的变量是本地变量,而不是本地变量。在所有宏中使用
    i
    本地解决了这个问题(不需要不同的名称)。它似乎不太可靠,如何跟踪使用了哪些名称?尤其是当你和别人分享的时候。仅仅确保它们被设置为本地变量有什么错?我很愿意接受你的答案,因为你发现了问题,但我不喜欢你的额外建议:)@Moody\u mudscappper嗯,你可以看到
    %put\u user\uu
    使用的所有变量,其中列出了所有正在使用的变量。我明白你的意思。应该有更可靠的方法。回答得好,汤姆。我也一直很欣赏关于格式化和如何改进我的便利功能的提示。更新以消除extract_pos中的
    %if
    ,并且只使用-1作为
    %do j
    循环的上限。这样,当令牌为单值时,do循环仅覆盖一个值<代码>%do j=%scan(&token,1,:)%到%scan(&token,-1,:)
    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