SAS 全局、局部宏變量
,其中具體講解了 R 中,函數(shù)內(nèi)外作用域之分。
那么,在 SAS language 中,是否同樣存在類似的機(jī)制,使得 variables 的調(diào)用處理有內(nèi)外之分?
答案是 yes。
初初接觸 SAS 的朋友,似乎覺(jué)得宏 (macro) 有點(diǎn)難以親近。依照我們學(xué)習(xí)的方法來(lái)講,作類比可以很好的幫助理解。那么,實(shí)質(zhì)上,SAS 中的宏,我們可以就當(dāng)作 R 中的函數(shù)來(lái)理解,本質(zhì)上它們也是在做一樣的事情。
既然如此,R 中函數(shù)有內(nèi)部外部之分,SAS 的宏自然也就不能免俗。具體體現(xiàn)在,SAS 中宏變量分為全局宏變量與局部宏變量。我們知道,R 中有環(huán)境這樣的機(jī)制,SAS 也是一樣,所謂的全局和局部宏變量的差別,就是指是處在整個(gè) SAS session 的環(huán)境之中,還是只位于某個(gè)或某些 macro definition 的環(huán)境之中。
所處環(huán)境不同,你的作用和地位自然也就不同。對(duì)于全局宏變量,顧名思義,global available,只要你還處于同一個(gè) SAS session 之中,你就可以隨時(shí)隨地調(diào)用、更新或解析(唯一不能在數(shù)據(jù)行中調(diào)用);而對(duì)于局部宏變量來(lái)說(shuō),local available,它的調(diào)用、更新或解析局限在所定義的 macro 之中,離開(kāi)這個(gè) macro,它就是無(wú)源之水、無(wú)本之木,無(wú)法生存,即徹底失效,無(wú)法在別處利用。
了解了這一點(diǎn),對(duì)我們 programming 有什么幫助?答,能夠讓你更加精細(xì)地操縱 code。
先來(lái)看一個(gè)例子,
%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;
我們有兩個(gè) macro,其中 func2 中調(diào)用了 func1。欲實(shí)現(xiàn),完成兩個(gè) macro 各自的 %do loop,而實(shí)際的運(yùn)行結(jié)果如下,

并不是我們期待的結(jié)果,func1 和 func2 的 %put 均只進(jìn)行了一次。why?
我們來(lái)庖丁解牛,深入其中紋理。當(dāng) func2 運(yùn)行時(shí),%do loop 中出現(xiàn)了宏變量 i,以 SAS macro 的邏輯,SAS 需要先在 func2 的局部環(huán)境里搜索宏變量 i 是否已存在,答案是 no,在 %do loop 之前,我們?cè)?func2 中并沒(méi)有創(chuàng)建這個(gè)宏變量 i,既然在 func2 的局部環(huán)境里找不到,SAS 會(huì)自動(dòng)跳轉(zhuǎn)到 func2 更上一級(jí)的環(huán)境之中即全局環(huán)境里去搜索宏變量 i,答案仍然是否定的,此時(shí),由于四處碰壁,SAS 自主地回到 func2 的局部環(huán)境中創(chuàng)建宏變量 i,以供 func2 的 %do loop 使用。
好了,現(xiàn)在 func2 的局部宏變量 i 被 %do loop 賦值為 1,進(jìn)入迭代。繼而調(diào)用 func1 這個(gè)宏,SAS 此時(shí)進(jìn)入到 func1 的局部環(huán)境之中,同樣地,func1 仍然需要搜索 func1 的局部宏變量 i,如你所想,func1 的局部環(huán)境中并未創(chuàng)建宏變量 i,怎么辦?SAS 跳轉(zhuǎn)到 func1 更上一級(jí)的環(huán)境之中,
注意??,這里 SAS 的環(huán)境機(jī)制和 R 語(yǔ)言是類似的。
SAS 的環(huán)境機(jī)制,我總結(jié)為如下的鏈?zhǔn)降燃?jí)繼承關(guān)系:
需要在當(dāng)前環(huán)境創(chuàng)建宏變量:
各個(gè)環(huán)境之間存在鏈?zhǔn)降燃?jí)繼承關(guān)系,即,由當(dāng)前環(huán)境逐級(jí)向上級(jí)父環(huán)境搜索某宏變量 a,若在某級(jí)父環(huán)境中存在同名宏變量 a,則在當(dāng)前環(huán)境中創(chuàng)建、賦值一個(gè)新的同名宏變量 a 并同步更新父環(huán)境中同名宏變量 a 的值,二者取值相同,全局環(huán)境為最大父環(huán)境;若經(jīng)搜索,在所有環(huán)境中均不存在同名宏變量 a,則返回當(dāng)前環(huán)境,只在當(dāng)前環(huán)境中創(chuàng)建宏變量 a 并完成賦值。
需要在當(dāng)前環(huán)境調(diào)用宏變量:
各個(gè)環(huán)境之間存在鏈?zhǔn)降燃?jí)繼承關(guān)系,即,由當(dāng)前環(huán)境逐級(jí)向上級(jí)父環(huán)境搜索某宏變量 a,若在某級(jí)環(huán)境中存在同名宏變量 a,則解析調(diào)用該級(jí)環(huán)境宏變量 a 的值,全局環(huán)境為最大父環(huán)境;若經(jīng)搜索,在所有環(huán)境中均不存在同名宏變量 a,則在 log 中生成該宏變量 a 未被成功解析的 warning。
func1 由于是在 func2 的 definition 之中被調(diào)用,那么 func1 局部環(huán)境的上一級(jí)環(huán)境 (父環(huán)境) 為 func2 的局部環(huán)境,有點(diǎn)繞,朋友你保持耐心,SAS 突然發(fā)現(xiàn),在 func2 父局部環(huán)境中已經(jīng)有了一個(gè)同名宏變量 i,則 SAS 在 func1 局部環(huán)境中創(chuàng)建、賦值一個(gè)新的同名宏變量 i 并更新 func2 父局部環(huán)境中同名宏變量 i 的值,二者取值相同,func1 的 %do loop 對(duì) i 賦值為 1,進(jìn)入 %do loop,結(jié)束 func1 的整體 %do loop 之后,func1 的局部宏變量 i 為 4,注意,根據(jù)鏈?zhǔn)降燃?jí)繼承關(guān)系,func2 的 i 值會(huì)被同步覆蓋更新,所以,當(dāng) SAS 重新進(jìn)入 func2 的局部環(huán)境之中時(shí),突然發(fā)現(xiàn) i 的值已經(jīng)被更新為 4,已然跳脫出 func2 %do loop 的取值范圍 1-3,因而,func2 的 %do loop 就此結(jié)束,最終結(jié)果只能是 func1 只被運(yùn)行了一次,func2 的 %put 也只進(jìn)行了一次。
經(jīng)過(guò)這個(gè)例子,我想,全局和局部之分的重要性已然明朗,忽視這份細(xì)節(jié),相比于你的期望結(jié)果,錯(cuò)誤是一定會(huì)發(fā)生的。
那么上面這個(gè)例子怎么改呢?
%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;

