簡單來說,call symput 與 call symputx 都可以在 data step 當中,將資料寫入 macro variable (宏變量)。語法如下:
- call symput(macvar, dsvar);
-
- call symputx(macvar, dsvar, "G/L/F");
复制代码
其中 macvar 是想要儲存資料的宏變量名稱。內容可以是 (1) 一個字串(用引號包裹),該字串必須是一個合法的宏變量名稱,或者 (2) 一個字串變量的名稱(不用引號包裹),取該字串變量的「內容」作為宏變量的名稱。
dsvar 是想要存入宏變量的內容。同上理,它也可以是一個引號包裹的字串,或者一個字串變量的名稱。
至於 call symputx 的 "G", "L", "F" 代表宏變量的 scope(如后解釋)。引號為必須。
"G": global
"L": local
"F": the most local macro varialbe(亦即,只要 local 的 macvar 存在,則將 dsvar 之值存到 local macvar 中,global macvar 則不受影響。若不存在,則存入 global 的 macvar。)
這兩者的共同點是:
1.都必須在 data step 之中呼叫(含 data _null_;)
2.都是將字串內容寫入 macro variable,數字則視同字串。
3.都不會自動清除首尾的空白字符。
兩者最大的區別,在於宏變量儲存的 scope。所謂 scope ,就是「變量存在的有效範圍」。宏變量可以依據其 scope 大致分成兩類:一類是 global (全域) 變量,它們會一直存在直到整個 SAS 系統結束運行,因此可以被任意程序在任意位置所使用;另一種是 local (局域) 變量,通常由 macro 當中的 %local 關鍵字所定義(參看後面範例)。它們僅能夠在該 macro 當中使用。一旦 macro 結束運行,其他程序便無從知曉它們的存在。
根據 SAS 9.4 官方文件, call symput 會自動把宏變量儲存在「"most local" "nonempty" symbol table」(最內層的非空符號表)裡頭。所謂 symbol table ,可以想像成是 SAS 用來儲存宏變量的一紙清單,並且不同的 scope 有不同的變量清單。當然,對於那些並不使用 local macro variable 的程式來說,這張「最內層的非空符號表」其實就是指 global symbol table,因此在這種條件下, call symput(macvar, dsvar) 就會等同於 call symputx(macvar, dsvar, "G")。
但是,如果您需要把程式碼封裝成 macro,就應該特別注意 scope 的問題。如果您在一個 macro 裡頭,使用了 call symput,而這個 macro 又剛好使用了 local macro variable,造成 local symbol table 不是空的,那麼您就一定要使用 call symputx(macvar, dsvar, "G") 來創建 global macro variable。否則,如果誤用了 call symput(),就會存成 local macro variable,macro 外面的程式碼是讀不到的。如下範例程序碼所示(註解都寫在裡面了,相信可以望文生義)。
- /* the test dataset */
- data a;
- input ID $ x;
- datalines;
- A 1
- B 2
- ;run;
- %macro mac_scope();
- /* make the local symbol table non-empty */
- %local x;
- %let x=1; /* create a local macro variable x=1 */
- /* assign macro variables using symput() and symputx() */
- data _null_;
- set a;
- call symput('mac', ID);
- call symputx('macx_local', ID, 'L');
- call symputx('macx_global', ID, 'G');
- stop; /* read the first obs */
- run;
- /* test macvar existence in the local symbol table */
- data _null_;
- if symexist('mac') then put "mac exists locally";
- else put "mac does NOT exist locally";
- if symexist('macx_local') then put "macx_local exists locally";
- else put "macx_local does NOT exist locally";
- if symexist('macx_global') then put "macx_global exists locally";
- else put "macx_global does NOT exist locally";
- run;
- %mend mac_scope;
- /* macro execution */
- %symdel mac macx_local macx_global; * clear previous results;
- %mac_scope();
- /* test macvar existence in the global symbol table */
- data _null_;
- if symexist('mac') then put "mac exists globally";
- else put "mac does NOT exist globally";
- if symexist('macx_local') then put "macx_local exists globally";
- else put "macx_local does NOT exist globally";
- if symexist('macx_global') then put "macx_global exists globally";
- else put "macx_global does NOT exist globally";
- run;
复制代码
執行完畢後,應該能夠看到這樣子的輸出(tested on SAS 9.4 / win7 x64)。
mac exists locally
macx_local exists locally
macx_global exists locally /* locally 都沒什麼問題 */
mac does NOT exist globally
macx_local does NOT exist globally
macx_global exists globally /* 很清楚了:當 local symbol table 非空,就只有 call symputx( , , 'G') 才能建立全域宏變量 */
因此個人建議:最好永遠使用 call symputx() 來明確指定宏變量的 scope,並且小心使用全域宏變量。call symput() 則視為過時語法,避免使用。這是因為 SAS 的程序在封裝成 macro 之後,除蟲變得不易,並且程序一大,變量撞名機率的機率便會升高....因此寧可在撰寫程序的時候,多打一個參數,然後多花個幾秒,想想是否真的需要建立一個全域宏變量。否則除蟲的時候就會感慨,偷雞不著蝕把米啊。
最後!差點忘了 call symputn()。它是 SCL (SAS Component Language) 專用的語法,一般的 data step 是不能用的。當然,顧名思義,它是 call symput 的數字版本,可以直接把一個數字(非數字字串)存入宏變量。但如果沒有機會接觸 SCL,直接當這個語法不存在就是了。
參考資料:SAS 9.4 官方說明文件。