漫談SAS Macro (3) - Macro Quoting, Part III
前文提到了%QUPCASE這個函數。SAS的幫助文檔中提到了一個十分有意思的例子:
%let a=begin;n%let b=%nrstr(&a);n%put UPCASE produces: %upcase(&b);n%put QUPCASE produces: %qupcase(&b);n================================================================================nUPCASE produces: beginnQUPCASE produces: &An
如果你對結果感到吃驚,先看一下這裡的解釋。前文提到%QUPCASE與%UPCASE的區別在於前者在運行期間會禁止宏和宏變數的解析。這個例子裡面,因為&b的屏蔽作用發生在編譯期間,所以兩者作用後的結果都是&A(字元a被大寫了)。不同的是,&字元在%QUPCASE作用後仍然保持屏蔽狀態,而在%UPCASE作用後屏蔽作用就消失了,故而&A被進一步解析成了begin。稍後會給出另一個更深入一點解釋,這裡我們先用SASHELP.VMACRO來檢查一下%QUPCASE的作用結果:
%let a=begin;n%let b=%nrstr(&a);n%let c=%qupcase(&b);nndata _null_;n set sashelp.vmacro;n if name=C then put value $hex.;nrun;n================================================================================n060F4108 (注意:原始結果的末端的空格已經被人工刪除)n
現在我們可以不用假裝明白了,問一個從這個系列第一個例子就開始不解的問題:為什麼屏蔽後的宏變數用%PUT語句列印時,仍然顯示原值?
這其實是Macro Quoting作用機制的外在邏輯--Transparent(透明)。之前在逛知乎的時候看到有個答案裡面說,計算機領域的「透明」有時候和一個人第一次看到這個詞所理解的意思正好相反。所以在這裡稍微解釋一下:假設我們把內部作用機制看作是一個盒子,當我們說「透明」時,我們指的是整個盒子都是透明看不見的,而不是指盒子的外壁是透明的(可以看到盒子內部)。透明的好處是在解決一個問題的同時,把作用機制和細節隱藏起來。假設你現在把這個系列三篇文章所有的內容都忘掉,下面這個例子實際上是很神奇而又自然的:
%let a = %bquote(Im hungry);n%put &a;n================================================================================nIm hungryn
使用了%bquote之後宏變數a就可用了,而且它的值就是你想要的,不多也不少。打開SYMBOLGEN這個option,可以在log裡面看到:
option symbolgen;n%put &a;n================================================================================nSYMBOLGEN: Macro variable A resolves to Im hungrynSYMBOLGEN: Some characters in the above value which were subject to macro quoting have been unquoted for printing.nIm hungryn
如果你回想我們最開始為什麼會需要這些quoting function的話,你會想起這些函數的共同目的都是為了消除歧義。你需要向SAS解釋:這裡的&字元只是一個普通字元,不是宏變數的trigger,那裡的單引號只是一個普通字元,不是一段引用的開始。一旦你澄清了這些歧義,SAS就會按照澄清後的含義「使用」這些宏變數(字元串)。因為前文沒有引入WORD SCANNER這個概念,所以在這裡換一種在實用上無妨表述方式:
一旦你解釋清楚歧義之後,SAS向其他部分傳遞宏變數的值時,使用的是原值。
比如本文開始的例子,可以認為傳入%UPCASE和%QUPCASE的值都是字元串」&a". 注意這裡的&不是宏變數的trigger,而是一個普通的字元。經過%QUPCASE作用之後,字元a變成了A,而&繼續被顯式地解釋為普通字元(這正是%Qxxx類函數的作用嘛)。而經過%UPCASE作用之後,除了字元a被大寫成A,&不再繼續被解釋成普通字元,而是被當成了宏變數的trigger。SAS尋找到了宏變數A的值,於是&A解析成了begin. 類似於%UPCASE,函數%INDEX和%SUBSTR作用之後,原本處於屏蔽狀態的字元屏蔽作用消失。這種去屏蔽作用叫做unquote。上面的黑體字結論指出,SAS向其他部分傳遞值時自動發生unquote過程。
聽上去有道理,但是你可能會問:
%let a = vara;n%let b = %nrstr(&a);n%let c = &b;n%put &c;n================================================================================n&an
為什麼&c列印出來的值是&a?既然傳遞的是原值,第三個%let語句也並沒有顯式的指明要保持特殊字元的屏蔽狀態,為什麼宏變數c存儲的是字元串「&a」而不是宏變數a的值呢?
這裡的原因是,用%let語句構造宏變數時,始終是由word scanner和macro processer在工作,並沒有發生值的傳遞(有關這部分的詳述,因為沒有在這個系列中引入word scanner的概念,感興趣的話可以參考SAS幫助文檔中How SAS Processes Statements with Macro Activity章節)。如果改用別的方式構造宏變數,那麼效果就不一樣了,繼續上面的例子:
data _null_;n call symput(d, "&b");nrun;n%put &d;n=======================================================================nvaran
可以看到,一旦發生值的傳遞,unquote自動發生。在這個例子中,傳遞發生在macro processor和data步之間。
到這裡,有關於quoting function就暫時講完了。下一篇的主題,等想好了,更新到這裡。
推薦閱讀: