tft每日頭條

 > 生活

 > sas如何檢驗兩個變量間有相互作用

sas如何檢驗兩個變量間有相互作用

生活 更新时间:2025-02-15 08:38:44

sas如何檢驗兩個變量間有相互作用(Macro-宏變量的作用域)1

所謂宏變量的作用域(Scope),通俗地講就是宏變量可見性(Visibility)和可訪問性(accessibility).定義或者申明了一個宏變量之後,在什麼樣的環境下可以訪問這個宏變量(的值)呢?

1. 從簡單的例子談起

我們用漫談SAS Macro (1)中的示例macro來說明什麼是scope。先看version 1:

%macro getNobsOf(data); /* version 1 */

在調用macro getNobsOf之前,我們事先用%GLOBAL語句聲明了一個全局宏變量,并且在調用宏之後利用%put語句打印該宏變量的值(結果是19)。通過這個例子可以知道,在一個宏的内部可以訪問外部的宏變量,并且可以更新外部宏變量的值。

如果在調用之前不事先聲明全局宏變量nobs(并且當前環境中亦不存在同名的全局宏變量),那麼在執行%put語句的時候,會産生warning:

WARNING: Apparent symbolic reference NOBS not resolved.

這說明,在宏的外部無法訪問宏内部的同名宏變量。這就是所謂的作用範圍(Scope)的意思。

SAS宏變量作用範圍隻有有兩種,一種是全局的(global),另一種是局部的(local)。全局的宏變量一旦聲明或者定義之後,可以在當前SAS Session的任何地方,不管是open code還是macro當中訪問,而局部變量則隻能在特定的範圍内訪問。

1) 任何在open code當中定義的宏變量一律都是全局的,不管定義的方式是通過%let語句,還是通過call symput (DATA步), 抑或select語句的into從句(into clause of select statement, SQL).

2) 任何在Macro内部定義的宏變量默認都是局部的,不過使用上述何種方式定義。局部宏變量随着macro運行的結束就被釋放了,無法在macro以外的地方直接訪問。注意這是默認行為,和1)不同的是,我們可以在宏内部使用%global語句聲明宏變量為全局的。(順便說一句,如果你要做一個和上面的version 1類似的,通過宏變量和外部交換值,那麼可以考慮将該變量在宏的定義中聲明為全局的,這樣調用前就不用擔心因為作用域帶來的訪問問題了)。

2. %local語句是幹什麼的?

version 2的定義當中出現了%local語句:

%macro getNobsOf(data); /* version 2 */

先說結論,%local語句的作用是顯式地聲明宏變量的作用範圍是局部的。但是既然1) open code當中宏變量都是全局的,2) macro當中的宏變量默認都是局部的,那麼%local語句豈不是多餘的?我們把version 2簡化成下面的例子:

%let nobs = 19;

打印的結果是19,而我們都知道如果macro定義中去掉%local nobs,那麼打印結果就是20(參見 version 1)。之所以産生這樣的結果是因為%local實際上在macro内部創建了一個局部宏變量nobs,macro内部訪問以及更新操作都是對這個局部宏變量而言的。實際上,macro在執行當中遇到宏變量需要解析的時候,會遵循就近原則,優先解析該作用域内的宏變量,如果該作用域内不存在該宏變量,則向上遞歸查找該宏變量,直至最外層,如果找不到則在當前作用域内創建該宏變量。

可以利用SASHELP這個library當中的view - VMACRO來驗證:

%let nobs = 19;

沒有%local語句的時候,打印的結果隻有一行,宏變量nobs的作用範圍是GLOBAL,值是20.

%let nobs = 19;

可以看到加上%local語句的之後,在macro運行時,實際上存在着兩個同名的宏變量,一個的作用範圍是GLOBAL,一個的作用範圍是CHGNOBS(即該macro内部,也就是局部的),兩者的值。%local語句在macro内部聲明一個局部宏變量,該宏變量在macro内部屏蔽了外層作用域的同名宏變量。屏蔽的意思是,在macro内部不能訪問外層作用域的同名宏變量的值了。另外,局部宏變量的作用範圍用macro的名字來定義。(後面我們會講遞歸,到時候再回過頭來讨論下一局部宏變量的作用域)。

3. 屏蔽的意義在哪裡?

在什麼情況下我們需要主動去屏蔽外層的同名宏變量?還是通過一個例子來說明。假定我們需要寫一個macro,打印某library下面所有dataset中字符型變量的值的最長長度。為了我們的讨論,我們把這個過程拆分成兩層循環:第一層循環打印用于遍曆訪問library下所有(non-empty)的dataset,另一層循環遍曆某個dataset的字符型變量,并打印其最長長度。

%macro getMaxCharLen(data);

%macro loopData(lib);

外層循環macro調用了内層循環macro,這被稱作macro的嵌套(nesting).實際上如果你補充完宏getMaxCharLen的話然後調用loopData的話,你會發現這個宏根本停不下來--它陷入了死循環(infinite loop)。具體的原因就留給各位看官了,我隻指出,至少需要在getMaxCharLen的内部将循環變量i使用%local來聲明。

可能你會想,如果使用不同的循環變量不就行了嗎?是的,在這個例子當中,确實可以解決問題。但是現實的情況是,1) 有的時候你不清楚你寫的一個包含循環的macro會被其他人或者自己用在某些什麼其他場合,該場合可能包含外層循環,而你不能事先知道外層循環變量的名字,即便知道了要想避開同名也總是一件麻煩的事情; 2) 即便是外層不包含循環,一旦外層macro裡有和内層macro同名的宏變量,這些宏變量的值就會被意外的修改,從而引發可能的bug。

所以,好的習慣是,在定義宏的時候,用%local聲明每個宏變量,unless there is a higher purpose of not doing so.

End.

作者:魚木

來源:知乎

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关生活资讯推荐

热门生活资讯推荐

网友关注

Copyright 2023-2025 - www.tftnews.com All Rights Reserved