如何在更新宏变量的数据步骤中调用宏并立即使用该值?

How to call a macro in a data step that updates a macro variable and use that value immediately?

下面的例子非常简单,可以用更简单的方法解决。但是,我有兴趣让它发挥作用。以下示例基于 sashelp-library 的 cars-dataset。首先,我有一个名为 fun 的宏:

proc contents data = sashelp.cars out = mycontents;
run;

%macro fun(var);
proc sql noprint;
        select count(distinct(&var.))
        into :obs
        from sashelp.cars;
quit;
%mend;

现在我想调用宏但只是为了更新 obs(从输入语句)。我使用:

data work.test;
set mycontents;
if name ne "Type" then do;
      call execute('%nrstr(%fun('||name||');');
      new = &obs;
end;
else new = 5;

运行;

简而言之,这应该迭代 mycontents 的行。然后根据名称调用一个(多个)宏,这会更新 obs。然后我可以简单地用 obs 填充新列 new。但是,obs 对所有名称保持相同的值,即最后一个变量的值。

这里的问题是双重的。

首先,您不能在此上下文中使用 CALL EXECUTE,因为它直到 数据步骤完成 运行 后才会执行:所以任何取决于在 &obs 上将无法获得更新的值。你必须使用 dosubl.

其次,如果要获得更新值 mid-data 步骤,则需要使用 symget('obs'),而不是 &obs&obs会在编译数据步骤时解析,因此在执行过程中无法更改;但是 symget(obs) 指示数据步骤在执行期间查询符号 table。

这是一个使用 dosubl 执行此操作的示例,与您的示例相比变化很小。请注意 %global 语句以确保 obs 在数据步骤中对我们可用(还有其他更好的方法可以将其取回 - 即,将其包装在 fcmp 函数并使用 run_macro - 但这是最​​接近你如何做的)。

proc contents data = sashelp.cars out = mycontents;
run;

%macro fun(var);
%global obs;
proc sql noprint;
        select count(distinct(&var.))
        into :obs
        from sashelp.cars;
quit;
%mend;

data work.test;
set mycontents;
if name ne "Type" then do;
      rc = dosubl(cats('%fun(',name,')'));
      new = symgetn('obs');
end;
else new = 5;
run;