5

在尝试解决与宏变量范围相关的问题时,我发现这个非常有用的 SO 页面。 为什么 %let 不创建局部宏变量?

因此,总结一下,编写%let x = [];%do x = [] %to [];在宏中将:

  • 如果全局符号表中已经没有“x”,则创建一个局部范围的宏变量 x,或者
  • 如果“x”在全局符号表中,则更新全局范围宏变量“x”

这让我觉得非常不直观。由于这种设计选择,我愿意打赌 SAS 荒野中存在大量错误。我很少在宏中看到 %local 语句,即使是在使用常见变量名(如“i”或“counter”)的循环语句之上。例如,我刚刚从 SUGI 和 SAS 全球论坛论文 http://www.lexjansen.com/cgi-bin/xsl_transform.php?x=sgf2015&c =杉

事实上,我在我打开的第一篇 SAS 会议论文中发现了这段代码:

%macro flag;
data CLAIMS;
 set CLAIMS;
 %do j= 1 %to 3;
 if icd9px&j in (&codelist)
 then _prostate=1;
 %end;
run;
%mend;
%flag;

http://support.sas.com/resources/papers/proceedings15/1340-2015.pdf

任何调用 %flag 并且也有自己的 &j 变量的人都有祸了。他们很容易以没有日志错误但错误的结果结束,因为在他们调用 %flag 之后,他们的 &j 到处都是 4,这将是(根据经验)一个没有乐趣追踪的错误。或者更糟糕的是,他们可能永远不会意识到他们的结果是虚假的。

所以我的问题是,为什么不默认所有宏变量都是本地范围的决定?SAS 宏变量范围的工作方式是否有充分的理由?

4

3 回答 3

4

在不了解宏语言的历史的情况下,回答为什么以这种方式定义的范围规则对我来说很难。

当我学习宏语言(在 6.12 上)时,我很幸运从一开始就被教导宏应该始终将其变量声明为 %LOCAL,除非他们有充分的理由不这样做。有时,如果宏 var 未声明为 %local 或 %global,我什至会/* Not Local: MyMacVar */在其中添加注释以说明我不打算声明范围(这很不寻常,但有时很有用)。看到没有将变量声明为 %LOCAL 的 UG 论文、SO 答案等让我很痛苦。

我猜测(这只是一个猜测),有一些早期版本的 SAS 有(全局)宏变量用于代码中的文本生成,但没有宏。所以在这样的版本中,人们会习惯于拥有大量的全局宏变量,以及相关的问题(例如碰撞)。然后,当 SAS 设计宏时,就会出现这样的问题:“我可以从宏内部引用我的宏变量吗?” 设计师选择回答“是的,您不仅可以引用它们,还可以为它们分配值,我会通过默认情况下允许您这样做来简化它。而且,宏将创建自己的范围可以保存局部宏变量。如果您引用一个宏变量或分配一个与全局范围(或​​任何外部范围)中存在的宏变量同名的宏变量,我'

从当前宏语言/宏开发人员的角度来看,大多数人认为应该避免大多数全局宏变量。宏语言的好处之一是它提供了允许模块化/封装/信息隐藏的宏。从这个角度来看,%local 变量更有用,而没有声明为 %local 的宏变量是对封装的威胁(即碰撞威胁)。所以我倾向于同意,如果我重新设计宏语言,我会默认将宏变量设为 %local。但当然,在这一点上,改变已经太迟了。

于 2016-02-21T11:36:45.247 回答
4

很大程度上,因为 SAS 是一种 50 年历史的语言,在明确首选词汇范围之前就已经存在。

SAS 混合了两个范围概念,但主要是动态范围,除非您有意更改它。这意味着仅仅通过阅读函数的定义,您无法知道在运行时哪些变量可供它使用。和赋值语句适用于当前在运行时可用的变量版本(而不是强制在可用的最本地范围内)。

这意味着宏编译器无法判断一个特定的赋值语句是打算分配一个局部宏变量,还是一个可能存在的运行时更高范围的宏变量。SAS 可以按照您的说法强制执行局部宏变量,但这会将 SAS 变成一种词法范围语言,基于与过去的一致性(保持向后兼容性)和基于功能,这都是不希望的;SAS 提供强制词法范围(使用%local)的能力,但不提供在更高范围(某种形式的parent?)中故意更改变量的能力,而不是%global.

请注意,动态范围在 60 年代和 70 年代非常普遍。S-Plus、Lisp 等都有动态作用域。SAS 倾向于尽可能向后兼容。SAS 也是常用的分析师,而不是程序员,因此需要尽可能避免复杂性。它们%local为我们这些确实想要词汇作用域优势的人提供

于 2016-02-22T16:14:57.583 回答
0

然后我们不能这样做,或者至少不能没有新的声明性声明。

33         %let c=C is global;
34         %macro b(arg);
35            %let &arg=Set by B;
36            %mend b;
37         %macro a(arg);
38            %local c;
39            %b(c);
40            %put NOTE: &=c;
41            %mend a;
42         %a();
NOTE: C=Set by B
于 2016-02-22T10:34:27.557 回答