2018年1月10日 星期三

macro應用

問題:把一個數字套用format容易,但若要用macro facility來處理呢?
說明:
在data step要套用format,直覺想到以下

data a;
  a=1.5;
  b=put(1.5,z5.2);
  put b; 
  put a z5.2 ;
run;

若要把上面put出來的01.50弄成macro variable,需要再套一個call symput指令。
後來摸過%SYSFUNC(),它可以套用dataset的function,就摸索出以下方式

  • %put %SYSFUNC( abs(1.5), z5.2) ;放一個能得到1.5的function,用簡單的abs就好
  • %put %SYSFUNC( putn(1.5,z5.2)) ;

但請注意下列這種無法用

  • %put %SYSFUNC(1.5, z5.2) ; 這個錯在sysfunc沒有指定function,只有給1.5。
  • %put %SYSFUNC( put(1.5,z5.2)) ; 目前不知為何,只知道sysfunc要配putn,putc,inputn,inputc才能用

也可以把要指定的format格式弄成macro variable,方便後續開發運用,如以下的例子

%MACRO now( fmt= DATETIME23.3 ) /DES= 'timestamp';  
  %SYSFUNC( DATETIME(), &fmt )  
%MEND  now ;  
%put %now;

ref: The Big Introduction from the Smallest Macro

這篇有一些東西還沒仔細看,A user-defined format that creates a date-time string that import wizards treat as a value.

PROC FORMAT ; 
   PICTURE  
       u_dt .= ' ' 
       OTHER ='%0Y/%0m/%0d %0H:%0M:%0S'( DATATYPE= DATETIME ); 
RUN ; 

問題:Quotes within Quotes ,有時候會錯亂? 說明:
下面例子呈現data step幾個原則

%let saying = Nevermore; 
data _null_; 
 Nevermore='test';
 
 a1='&saying';
 a1A_1='"&saying"';
 a1A_2='""&saying""';
 a1A_3='"""&saying"""';
 a1B_1='''&saying''';
 a1B_2='''''&saying''''';
 a1B_3='''''''&saying''''''';

 a2="&saying";
 a2A_1="'&saying'";
 a2A_2="''&saying''";
 a2A_3="'''&saying'''";
 a2B_1="""&saying""";
 a2B_2="""""&saying""""";
 a2B_3="""""""&saying""""""";

 put (a:) (=/);
run;

/*Log output 如下*/

a1=&saying
a1A_1="&saying"
a1A_2=""&saying""
a1A_3="""&saying"""
a1B_1='&saying'
a1B_2=''&saying''
a1B_3='''&saying'''
a2=Nevermore
a2A_1='Nevermore'
a2A_2=''Nevermore''
a2A_3='''Nevermore'''
a2B_1="Nevermore"
a2B_2=""Nevermore""
a2B_3="""Nevermore"""
  • 當最外層quotes是single quote,裡面的macro variable 就無法被 resolve,如a1系列。最外層是 double quote ,macro variable 就有可能被resovle,如 a2A_1。
    作者對此的註解是 during the assignment of the variable’s value, the inner single quotes are masked, not seen as parsing characters, so the macro variable resolves.
  • 當最外層quotes與內層quotes是同一種quote,在內層的quote就必須用連續2個quote為單位,此時,會把連續2個quote轉成1個quote,如對照一下 a1A_1 和 a1B_1 ,可發現雖然最後都是1個 single or double quote,但是 a1B_1 的內層是2個 quote ; 比較 a1A_3 a1B_3 就更明顯,a1B_3用了6個。
    要注意,連續2個quote轉成1個quote,此件事發生的時機為: when the parser sees two single (or double) quotes immediately following each other, the parser resolves them into one quote mark after the closing quote has been determined.

下面的例子,就只有 note1 的結果會和 note0 相同。

data _null_; 
 note0 = "The raven sayth: 'Nevermore'"; 
 note1 = "The raven sayth: '&saying'"; 
 note2 = 'The raven sayth: ''&saying'''; 
 note2_1 = 'The raven sayth: ''''&saying'''''; 
 note3 = "The raven sayth: ""&saying"""; 
 put (note:) (=/); 
run;

其他文章裡摘錄的重點

  • 要產生一個title 為 Tom's Truck 可用 title1 "Tom's Truck"; 或者是 title1 'Tom''s Truck';
  • 以前 X command 在 windows 系統下有點麻煩,X command 要用quote, 而 windows 的 path 一定要用 double quote,造成冏境 x 'dir "c:\&temp\*.sas"' ;
    &temp無法被 resolve。
    但現在X command 本身不必用 quote 了,可省下最外層的single quote。
  • DM STATEMENT 一定要有 quote,但 sas 9 開始它 can contain macro variables,並且第1個 quote 是用於把指令轉傳給 Display manager 去處理,因此 dm 'log; file "c:\&temp\logdump1.log"'; Display manager 只收到 log; file "c:\&temp\logdump1.log" , 當然可以 resovle &temp。 另個例子,dm "log; file 'c:\&temp\logdump1.log'"; 稍有不同,因為 file 收到的是 'c:\&temp\logdump1.log' ,第1次沒有轉,但因 file 會把 quote 當成 parsing characters within the command 所以 &temp 又可以轉了。
  • CALL DEFINE STATEMENT在用時,call define(_col_,'style', 'style={flyover="&temp Mean WT"}'); 是可以被 resovle 的。
  • FILENAME STATEMENT
    想要下的指令 filename tmpdat pipe 'dir "c:\&temp\*.sas" /o:n /b'; 但它無法被 resolve。本例的必要條件有2個,1. pipe 之後的 dir... 一定要放在 quotation mark 裡面,2. dir 之後的 path 一定要放在 double quotes 裡面。解法如下...

Using the DATA Step Quote Function
用 sysfunc + Quote Function 來替代條件1,最簡單
filename tmpdat pipe %sysfunc(quote(dir "c:\&temp\*.sas" /o:n /b));

Using Repeated Quote Marks
把外層換成 double quotes ,內層換成 doubled double quotes ,如此 the interior string is marked in a second pass of the parser, after the macro variable has been resolved.
filename tmpdat pipe "dir ""c:\&temp\*.sas"" /o:n /b";

Using Macro Quoting Functions
針對外層的 single quote ,用 macro quoting function 來處理,這有很多種,並且每種用途不太相同,常用的有

  • %STR(%')
  • %BQUOTE()

例如可把原式改為
filename tmpdat pipe %bquote(')dir "c:\&temp\*.sas" %bquote(/o:n /b'); 但這會有錯,因為 single quote 被蓋著,沒有 remove macro quoting ,後面的 dir 就被當成 unquoted option ,不符合條件1
可用 %UNQUOTE() 把整個 dir command 包起來處理
filename tmpdat pipe %Unquote(%bquote(')dir "c:\&temp\*.sas" %bquote(/o:n /b'));
整包送進去 macro facility 處理,最後要離開時會得到'dir "c:\My Loc\*.sas" /o:n /b',這串才能正確接著 filename statement 。具有 %LEFT %TRIM 也可以 remove macro quoting 但應用的時機不同。

ref: Quotes within Quotes When Single (‘) and Double (“) Quotes are not Enough


說明:put出日期有關的資訊

%put &sysdate.;
%put %sysfunc(today(),date9. );

%put &systime. ;
%put %sysfunc(time(),time5. );

%put %sysfunc(datetime(),datetime20. );


說明: CALL EXECUTE made easy for SAS data-driven programming

文中指出,基本的概念是:
As the DATA step iterates, the code is appended to the queue as many times as there are iterations of the DATA step. After the DATA step completes, the code in the queue gets executed in the order of its creation (First In First Out).

有 macro reference 的情況

  • macro reference in double quotes
    they will be resolved by the SAS macro pre-processor during the DATA step compilation. Nothing unusual.

  • macro reference in single quotes
    will be resolved by CALL EXECUTE itself. 同上也是在 pushed out of the DATA step 之前就會完成。
    因為 CALL EXECUTE 具有 macro resolution privilege

  • CALL SYMPUT or SYMPUTX statement (in a DATA step) or an INTO clause (in PROC SQL)
    WARNING: Apparent symbolic reference VARLIST not resolved.
    它們的 macro variable 還沒真的建立之前,就被要求要 resovle
    解決的方式可以直接把整個 statement 用 %nrstr 包起來 ,如:
    call execute('%nrstr(%onetable('!!strip(tname)!!'));');
    強迫推到 queue 裡,之後再 resolve macro
    NOTE: CALL EXECUTE generated line.
    1 + %onetable(ADDRESS);

  • 把 MACRO 的名稱也用 dataset variable 代入
    arg=cats('%nrstr(%', MNAME_variable, '(parameter=', Para, '))' );
    call execute(arg);

ref: https://blogs.sas.com/content/sgf/2017/08/02/call-execute-for-sas-data-driven-programming/