带有 if 和 include 的宏环境变量可见性

Macro environment variable visibility with if and include

美好的一天,

我 运行 进入以下情况,其中宏变量被定义为全局变量取决于它们是否来自 include ....在宏中定义的变量不应该在宏之外可见,对吧。这失败了:

%macro if_env;
    %let a=100;
%mend if_env;
%if_env;
%put &a.;

WARNING: Apparent symbolic reference A not resolved.

这符合预期。 但是,我 运行 遇到了一个问题,变量流向全局 space:

我有一个包含文件:C:\TEMP\test.sas,它只包含变量 a b c d 的设置:(用于测试目的)

%let a=100;
%let B=200;
%let C=300;
%let D=400;

这也按预期工作:

%macro if_env;
    %include"C:\TEMP\test.sas";
%mend if_env;
%if_env;
%put &B.;

WARNING: Apparent symbolic reference B not resolved.

到目前为止一切都很好。现在,我添加了 if 子句 'include if file exists'-condition:

%macro if_env;
   %if %sysfunc(fileexist(C:\TEMP\test.sas)) %then
   %let C=100
%mend if_env;
%if_env;
%put &C.;

WARNING: Apparent symbolic reference C not resolved.

最后一步:将实际包含添加到代码中:

%macro if_env;
   %if %sysfunc(fileexist(C:\TEMP\test.sas)) %then
   %include"C:\TEMP\test.sas";
%mend if_env;
%if_env;
%put &A.;
%put &B.;
%put &C.;
%put &D.;

100 200 300 400

嗯,不是计算。这不应该发生。 %let-command 怎么会有不同的名称space 取决于它是否在 include 中?

知道为什么会这样吗?是错误还是真正奇特的功能?

编辑: 很有意思。基于 SAS documentation 应该在包含的末尾使用分号。很高兴对此事作出澄清。感谢您的回答。

%include 语句以分号结束。

%if 语句以分号结束。

宏中的分号终止宏中的%if

  • %if 的计算结果为真时,%include 语句开始,但在调用后以分号结束。因此 %include 语句执行范围与 %put 语句相同。
  • 当 %if` 的计算结果为 false 时,调用后的分号只是空语句或独立的分号。

这种宏编程的微妙之处常常被忽视。

要强制 %include 出现在宏范围内,请使用

... %then %do; %include ...; %end;

... %then %include ... %str(;) ;

我不会称它为 SAS 中的错误,而是您程序中的错误。由于您的宏没有提供分号来终止 %INCLUDE 语句,因此宏在 %include 语句运行之前结束。因此局部符号 space 不再存在并且 %let 语句创建全局宏。

如果你格式化代码,使 SAS 代码和宏代码在不同的行上,它会更清晰。

%if 1=1 %then 
  %include test
;

添加 %do/%end 将允许您为 %include 提供分号。

%if 1=1 %then %do;
  %include test ;
%end;

如果您在另一个宏中调用此宏,则宏变量将创建为该调用宏的局部变量。所以这是一个技巧,可用于将宏变量推送到父环境中。

%macro if_env;
  %include test
%mend if_env;
%macro outer ;
 %if_env;
 %put _local_;
%mend outer;

打开 MLOGIC 选项以查看发生了什么。

235  options mlogic source2;
236  %outer ;
MLOGIC(OUTER):  Beginning execution.
MLOGIC(IF_ENV):  Beginning execution.
MLOGIC(IF_ENV):  Ending execution.
NOTE: %INCLUDE (level 1) file TEST is file /.../#LN00048.
237 +%let a=1;
238 +%let b=2;
239 +%let c=3;
NOTE: %INCLUDE (level 1) ending.
MLOGIC(OUTER):  %PUT _local_
OUTER A 1
OUTER B 2
OUTER C 3
MLOGIC(OUTER):  Ending execution.