SAS宏:循环创建变量[宏/引用问题]

SAS宏:循环创建变量[宏/引用问题],sas,sas-macro,Sas,Sas Macro,基本上,我正在编写一个宏,它将输入表、输出表和变量列表作为参数。我的变量列表显示为单个参数,我使用空格字符作为分隔符。 我的宏应该将我的列表分隔为nbvar宏变量,这些变量将包含我的(SAS)变量的名称。然后,我使用datastep将我的(SAS)变量从原始字符格式输入到数值 以下是我的代码: %macro convert_car_to_num(input,output,listvar); /* First I split my list into nbvar variables named

基本上,我正在编写一个宏,它将输入表、输出表和变量列表作为参数。我的变量列表显示为单个参数,我使用空格字符作为分隔符。 我的宏应该将我的列表分隔为nbvar宏变量,这些变量将包含我的(SAS)变量的名称。然后,我使用datastep将我的(SAS)变量从原始字符格式输入到数值

以下是我的代码:

%macro convert_car_to_num(input,output,listvar);

/* First I split my list into nbvar variables named var&i
%qscan to avoid macro resolution of names, not really necessary here
but still works fine. My delimiter is space character, hence 
%str( ) in the %qscan*/

%let nbvar=%sysfunc(countw(&listvar));
%do i = 1 %to &nbvar;
    %let var&i=%qscan(&listvar,&i,%str( ));
%end;

/*Here is my data step. &&var&i_num is resolved just fine*/
data &output;
set &input;
%do i = 1 %to &nbvar;
    &&var&i.._num = input(&&var&i,BEST16.);
%end;
run;

%mend;
由于&&var&i..u num和&&var&i已解析,我希望我的代码能够正常工作,但我的日志显示:

varname\u num

180

其中已解析的名称“varname”带下划线。在我发现之后:

错误180-322:语句无效或使用顺序不正确。

这通常是放错分号的标准错误。但我知道我的宏变量已解析,因为mprint显示:

MPRINT(将车转换为数):varname\u NUM=input(varname,BEST16)。

注意:由宏变量“VAR26”生成的行。

MPRINT(将车转换为数):运行

其中varname是我列表中第26个变量的正确名称,这表明解析工作正常

为了让我更难以理解,我在同一段代码中指出:

&&var&i.. = input(&&var&i,BEST16.);
进行编译,即使它没有得到预期的结果(变量仍然是char)

类似地,相同的代码具有:

&&var&i.._num = &&var&i;
也不编译

我还测试了将宏变量的名称更改为num&&var&I或n&&var&I,甚至首先声明一个包含&&var&I的宏变量“name”,所有这些都具有相同的效果。不选择与初始变量相同的名称似乎会导致代码显示180错误

我想问题在于试图声明一个变量,因为我知道我之前编写的一段类似代码确实有效,而datastep是一个比较(也可以将变量列表中缺少的值转换为零):

但对于同一段代码,如果我尝试创建一个新变量(同样是使用任何名称),则写:

if num_&&var&i = . then &&var&i = 0;
我发现自己的解析名称再次下划线,但现在指向以下错误:

错误22-322:语法错误,应为以下之一:!,!!,&,(,*,***,+,-,/,;,=,和,EQ,GE,GT,IN,LE,LT,MAX,MIN,NE,NG,NL,NOTIN,或,
[,^=,{,|,|,| |,~=。

以下内容是否满足了您的需要?我的猜测是,如果您在单独的
语句中执行
操作,您会遇到太多问题:

%macro new(input,output,listvar);
data &output; set &input;
%do i=1 %to %sysfunc(countw(&listvar.));
    %let var=%scan(&listvar.,&i.);
        var&i._num = input(&var.,BEST16.);
%end;
run;
%mend;

%new(have,want,&listvar.);
如果您正试图为循环的每个变量创建一个单独的宏变量,下面可能也会执行您想要的操作(虽然在这种情况下,这可能不值得,但只显示了另一种适用于其他应用程序的有用方法):


这是SAS无法自动取消引用值的问题。我学到的规则是,如果您的SAS代码(以MPRINT显示)看起来有效,但出现错误,请尝试取消引用

在您的情况下,更改为:

%unquote(&&var&i.._num) = input(&&var&i,BEST16.);
使代码正常工作。当然,根据您的评论,您可能不需要引入有问题的引号字符的
%qscan
。如果您将其更改为
%scan
,您将不需要
%unquote()
它,因为它首先不会被引用

也同意@Foxer使用单个宏变量将第i个变量存储在列表中的方法。还建议使用这些%局部变量以避免冲突。可能类似于:

%macro convert_car_to_num(input,output,listvar);
  %local i vari;

  data &output;
    set &input;
    %do i = 1 %to %sysfunc(countw(&listvar,%str( )));
      %let vari=%scan(&listvar,&i,%str( ));
      &vari._num=input(&vari,best16.);
    %end;
  run;

%mend;

这正是我想要的解释。我想在使用%qscan时格外小心,以确保潜在的其他用户不会“弄糟”使用奇怪的名称,但我没有完全意识到让%qscan实际引用以避免任何意外的解决方案意味着什么。唯一临时变量的想法很好,但实际上我需要循环之外的所有变量。至于创建唯一循环:%do I=1%到%sysfunc(countw(&listvar,%str());我确实认为这样更好,但由于我可能会与其他人共享这些宏,我更喜欢它们分解步骤,以避免混淆人们,并使它们更容易用于其他目的。不过,我仍然有一个疑问:如果我确实需要%qscan来避免分辨率不高,我是否仍然可以使用:%unquote(&&var&I..u num)我的猜测是否定的,因为不引用将导致潜在的不必要的解决方案。我错了吗?如果错了,我将如何处理?(说实话,这是一个混乱的局面,但我只是试图完全理解宏的工作原理和可用于防止意外结果的工具)尽管如此,非常感谢您给出了第一个正确的答案。我想我已经找到了上一个问题的答案,它似乎可以使用:%sysfunc(cat(&&var&I,_num))工作。问题是,sas编译器无法“自动”将带sas引号的宏变量与另一段字符串连接起来(这里可能使用了错误的术语,我仍然有点不清楚它是SASmacro还是SAS编译器,但我认为是后者)。但是使用函数cat可以做到这一点,因为编译器会理解它的意图。SAS宏作为一个“元层”永远不会停止惊讶(和耗尽)我认为你最后的评论是错误的。带有引号(蒙面)的宏变量可以用通常的方式将值连接到另一个值,因为即使使用隐藏字符,它仍然是文本。问题是宏语言应该在sas编译器获取它们之前的最后一步自动取消引用它们,有时这种情况不会发生。使用%sysfunc(catt())将强制取消引号,但%unquote()更清晰。有关更多详细信息,请参阅Ian Whitlock的优秀论文,例如Y
%unquote(&&var&i.._num) = input(&&var&i,BEST16.);
%macro convert_car_to_num(input,output,listvar);
  %local i vari;

  data &output;
    set &input;
    %do i = 1 %to %sysfunc(countw(&listvar,%str( )));
      %let vari=%scan(&listvar,&i,%str( ));
      &vari._num=input(&vari,best16.);
    %end;
  run;

%mend;