欢迎光临散文网 会员登陆 & 注册

SAS 全局、局部宏变量

2021-09-09 23:18 作者:陆震同学  | 我要投稿

熟悉 R 的朋友,自然不能错过这篇 一文搞懂作用域机制,其中具体讲解了 R 中,函数内外作用域之分。

那么,在 SAS language 中,是否同样存在类似的机制,使得 variables 的调用处理有内外之分?

答案是 yes。

初初接触 SAS 的朋友,似乎觉得宏 (macro) 有点难以亲近。依照我们学习的方法来讲,作类比可以很好的帮助理解。那么,实质上,SAS 中的宏,我们可以就当作 R 中的函数来理解,本质上它们也是在做一样的事情。

既然如此,R 中函数有内部外部之分,SAS 的宏自然也就不能免俗。具体体现在,SAS 中宏变量分为全局宏变量与局部宏变量。我们知道,R 中有环境这样的机制,SAS 也是一样,所谓的全局和局部宏变量的差别,就是指是处在整个 SAS session 的环境之中,还是只位于某个或某些 macro definition 的环境之中。

所处环境不同,你的作用和地位自然也就不同。对于全局宏变量,顾名思义,global available,只要你还处于同一个 SAS session 之中,你就可以随时随地调用、更新或解析(唯一不能在数据行中调用);而对于局部宏变量来说,local available,它的调用、更新或解析局限在所定义的 macro 之中,离开这个 macro,它就是无源之水、无本之木,无法生存,即彻底失效,无法在别处利用。

了解了这一点,对我们 programming 有什么帮助?答,能够让你更加精细地操纵 code。

先来看一个例子,

%macro func1;
   %do i= 1 %to 3;
   %end;  
   %put inside func1;
%mend;
%macro func2;
   %do i= 1 %to 3;
       %func1;
       %put func2;
   %end;
%mend;
%func2;

我们有两个 macro,其中 func2 中调用了 func1。欲实现,完成两个 macro 各自的 %do loop,而实际的运行结果如下,

并不是我们期待的结果,func1 和 func2 的 %put 均只进行了一次。why?

我们来庖丁解牛,深入其中纹理。当 func2 运行时,%do loop 中出现了宏变量 i,以 SAS macro 的逻辑,SAS 需要先在 func2 的局部环境里搜索宏变量 i 是否已存在,答案是 no,在 %do loop 之前,我们在 func2 中并没有创建这个宏变量 i,既然在 func2 的局部环境里找不到,SAS 会自动跳转到 func2 更上一级的环境之中即全局环境里去搜索宏变量 i,答案仍然是否定的,此时,由于四处碰壁,SAS 自主地回到 func2 的局部环境中创建宏变量 i,以供 func2 的 %do loop 使用。

好了,现在 func2 的局部宏变量 i 被 %do loop 赋值为 1,进入迭代。继而调用 func1 这个宏,SAS 此时进入到 func1 的局部环境之中,同样地,func1 仍然需要搜索 func1 的局部宏变量 i,如你所想,func1 的局部环境中并未创建宏变量 i,怎么办?SAS 跳转到 func1 更上一级的环境之中,

注意⚠️,这里 SAS 的环境机制和 R 语言是类似的。

SAS 的环境机制,我总结为如下的链式等级继承关系

  1. 需要在当前环境创建宏变量:

各个环境之间存在链式等级继承关系,即,由当前环境逐级向上级父环境搜索某宏变量 a,若在某级父环境中存在同名宏变量 a,则在当前环境中创建、赋值一个新的同名宏变量 a 并同步更新父环境中同名宏变量 a 的值,二者取值相同,全局环境为最大父环境;若经搜索,在所有环境中均不存在同名宏变量 a,则返回当前环境,只在当前环境中创建宏变量 a 并完成赋值。

  1. 需要在当前环境调用宏变量:

各个环境之间存在链式等级继承关系,即,由当前环境逐级向上级父环境搜索某宏变量 a,若在某级环境中存在同名宏变量 a,则解析调用该级环境宏变量 a 的值,全局环境为最大父环境;若经搜索,在所有环境中均不存在同名宏变量 a,则在 log 中生成该宏变量 a 未被成功解析的 warning。

func1 由于是在 func2 的 definition 之中被调用,那么 func1 局部环境的上一级环境 (父环境) 为 func2 的局部环境,有点绕,朋友你保持耐心,SAS 突然发现,在 func2 父局部环境中已经有了一个同名宏变量 i,则 SAS 在 func1 局部环境中创建、赋值一个新的同名宏变量 i 并更新 func2 父局部环境中同名宏变量 i 的值,二者取值相同,func1 的 %do loop 对 i 赋值为 1,进入 %do loop,结束 func1 的整体 %do loop 之后,func1 的局部宏变量 i 为 4,注意,根据链式等级继承关系,func2 的 i 值会被同步覆盖更新,所以,当 SAS 重新进入 func2 的局部环境之中时,突然发现 i 的值已经被更新为 4,已然跳脱出 func2 %do loop 的取值范围 1-3,因而,func2 的 %do loop 就此结束,最终结果只能是 func1 只被运行了一次,func2 的 %put 也只进行了一次。

经过这个例子,我想,全局和局部之分的重要性已然明朗,忽视这份细节,相比于你的期望结果,错误是一定会发生的。

那么上面这个例子怎么改呢?

%macro func1;
   %local i;
   %do i= 1 %to 3;
   %end;  
   %put inside func1;
%mend;
%macro func2;
   %do i= 1 %to 3;
       %func1;
       %put func2;
   %end;
%mend;
%func2;

显然,我们只需要让 func1 的局部宏变量 i 摆脱链式等级继承关系,从 func2 的父局部环境中独立出来,使其不再拥有 func2 的父局部环境,从而 SAS 只会在 func1 的局部环境中创建局部宏变量 i,而不会影响更新到 func2 中 i 的取值,解决办法就是通过 %local statement 断绝链式等级继承关系,事先在 func1 搜索局部宏变量时,让 SAS 不再跳转到父环境 func2 中进行搜索,而是显式地在 func1 局部环境中声明并创建独立的 func1 局部宏变量 i,此时,func1 中的 i 独立于 func2 中的 i,如此一来,各自 i 均能实现从 1 到 3 的赋值迭代,目标完成✅

今天算写得一气呵成了,非常感谢你能看到这里,如果你觉得写得还不错的话,求转发求关注,我们下篇文章再见👨🏻‍💻


SAS 全局、局部宏变量的评论 (共 条)

分享到微博请遵守国家法律