漫談SAS Macro (3) - Macro Quoting, Part II
前文最後一個例子
data _null_; n call symput(client, Johnson&Johnson); nrun; nn%let fullstr = %bquote(Client: &client); n%let fullstr2 = %nrbquote(Client: &client);n
無論是使用%bquote還是%nrbquote都會產生惱人的warning。試想你要為公司開發一套做TLF的macro,作為其中的一部分你需要允許你的用戶在TLF的title上面顯示公司的名稱。對於用戶傳入的宏變數client,你要怎麼做才能避免因為宏變數client解析出來的字元串裡面含有&或者%而產生的warning呢?
一種(最簡單的)方法是(告知)用戶在傳遞值時直接使用%nrstr屏蔽&和%:
%let client = %nrstr(Johnson&Johnson);n%let fullstr = %bquote(Client: &client); n%put &fullstr;n================================================================================nClient: Johnson&Johnsonn
稍微複雜一點的例子:假定我們編寫一個macro從ADaM Dataset的spec中讀取各variable的屬性(然後將屬性複製給用戶輸入的dataset)。作為一個嚴謹的programmer,你必須假定會碰到各種各樣的variable label:
data attrib; /* example dataset importing from ADaM spec */n length name $32 type $10 length 8 label $255;n name = AVAL; type = num; length = 8; label = Analysis Value; n output;n name = PCHG; type = num; length = 8; label = %Change from Baseline;n output;nrun;nndata _null_; /* copy variable attributes to macro variables */n set attrib;n call symputx(name||strip(put(_n_, best.)), name);n call symputx(type||strip(put(_n_, best.)), type);n call symputx(len||strip(put(_n_, best.)), put(length, best.));n /* mask special characters in variable label */n call symput(label||strip(put(_n_, best.)), %nrstr(||trim(label)||));nrun;nn%put &label2;n
注意第二個data步中最後一個call symput語句構造了一個%nrstr的表達式,試著去掉%nrstr看看有什麼結果。
再看一個例子,假定我們需要定義一個宏用來in-place(關於in-place的好處,參見漫談SAS Macro (1))返回輸入的SAS dataset的label:
%macro getDsetLabel(data);nn%local dsid;n%local label;n%local rc;nn%let dsid = %sysfunc(open(&data));n%let label = %nrbquote(%sysfunc(attrc(&dsid, label)));n%let rc = %sysfunc(close(&dsid));nn&labelnn%mend getDsetLabel;nndata test(label=%Change from Last Quaters Shipments);nrun;nn%put %getDsetLabel(test);n================================================================================nWARNING: Apparent invocation of macro CHANGE not resolved.n%Change from Last Quaters Shipmentsn
注意WARNING語句來源於
%let label = %nrbquote(%sysfunc(attrc(&dsid, label)));n
在這樣的宏裡面,我們實在是沒有理由認為或者假設輸入data的label做好了屏蔽特殊字元的準備。%nrbquote固然可以很好的避免因為label中包含未成對的括弧或者單雙引號而產生的問題,但是對於label中包含的%和&就無能為力了。幸運的是SAS引入了一系列Q開頭的macro function,在本例中使用%QSYSFUNC代替%SYSFUNC:
%let label = %qsysfunc(attrc(&dsid, label));n
可以屏蔽ATTRC函數獲取的label中包含的&或者%,從而達到我們最初設計macro的目的。
從這個例子裡面我們還可以看到%QSYSFUNC屏蔽的特殊字元與%NRBQUOTE相當,即各類特殊字元(包括&和%)以及非成對的括弧和單雙引號。而且不同於%NRBQUOTE函數試圖解析字元串中的宏變數(&)或者宏(%),%QSYSFUNC並不試圖解析它們。因此它對於避免程序中出現上面的WARNING非常有效。順便提一句,如果你的程序中恰好定義過宏%change,那麼避免SAS解析%change就不只是fix log中一個WARNING這麼簡單了。
類似於%QSYSFUNC的函數,還有%QSCAN, %QSUBSTR, %QUPCASE。它們的作用機理和作用效果與%QSYSFUNC類似,詳細的信息可以翻閱SAS的幫助文檔。
實際上,這個家族還有一個函數,那就是%SUPERQ。回到本文最開始的例子,使用%SUPERQ的版本是:
data _null_; n call symput(client, Johnson&Johnson); nrun; nn%let fullstr = Client: %superq(client); n%put &fullstr;nn================================================================================nClient: Johnson&Johnsonn
SUPERQ接受一個字元串作為參數,然後SUPERQ試圖在當前scope中尋找以該字元串為名字的宏變數,並屏蔽其值中包含的特殊字元(包括&和%)以及不成對的括弧和但雙引號。%SUPERQ同樣不會試圖解析宏變數的值中包含的宏變數或者宏。另外需要注意
%superq(client)n
的意思是解析並屏蔽宏變數&client,而
%superq(&client)n
的意思是解析並屏蔽以&client的值為名字的宏變數(如果&client的值是一個合法的宏變數名,並且該宏變數確實存在的話)。在本例中%superq(&client)會返回錯誤。
關於Quoting Function的例子大概就講這麼多了。從這些例子中,可以看到一旦宏變數被任一quoting function作用之後,再次引用該宏變數就無需再次使用quoting function了。這表明,屏蔽的作用是持續的,這一點我們也多次在例子中利用SASHELP.VMACRO證明了(屏蔽作用實際上是以字元替代的形式存儲起來的)。作為這個topic的結尾,下一節我們討論一下quoting的作用範圍和去作用化。
推薦閱讀: