同時に指摘いただいた4名の方々有難うございます。ちゃんと見てる人いるんですね、反省します..)
こないだ電車乗ってたら、ブログに長いタイトルつける奴は仕事できない奴だっていってるおじさんがいました。要点がまとめれていないからだそうです。僕もそう思います。
さて、もう言いたいことはタイトルで言ってしまったので、これ以上何もないのですが説明します。
例えば以前の記事で、SASには現状、文字型の非欠損値をカウントする変数がないって話をしました
http://sas-tumesas.blogspot.jp/2015/02/blog-post.html
それをマクロをサブルーチン的に使って解決しようと考えたとします。
以下のようなデータがあり
data Q1;
A='';B='a';C='a';D='a';E='a';output;
A='a';B='';C='a';D='';E='a';output;
A='';B='a';C='';D='a';E='';output;
A='a';B='a';C='a';D='a';E='a';output;
run;
以下のようにマクロを組んでみました。
%macro ar(varlist);
array AR $ &varlist;
CN=0;
do over AR;
CN + ^missing(AR);
end;
drop CN;
%mend ar;
早速そのマクロを使って、変数A B Cのうち値があるものの数をカウントしてみます。
options mprint;
data A1;
set Q1;
%ar(A B C)
count_A_B_C=CN;
run;
はい、できました。
ちなみにoptions mprintをつけたのでログにはこんな感じで展開されたものが
でています
data A2;
set Q1;
%ar(A B C)
count_A_B_C=CN;
%ar(C D E)
count_C_D_E=CN;
run;
はい、エラーになりました。
これはarマクロの中でarrayステートメントで配列名「AR」を生成してるんですが
arマクロを2回以上入れると、マクロが展開されたとき、1つのデータステップの中で配列「AR」を
複数回宣言することになります。そしてそれはSASの文法上NGなので通りませんぜと。
じゃあ、どうするか。
このマクロにとって、配列は、カウント処理の際のみに必要なものであって
CNが計算できた後は意味がなく、配列の名前についてもどうでもいいわけです。
たとえば、引数の変数名を全部文字連結して配列名にするようにしてもいいですが
配列名は32文字までなので、長い変数をたくさん指定すればエラーになってしまいます。
乱数とかでもいいですが、たまたま衝突する可能性は、まあ一応ゼロではありません。
じゃあ、パラメーターひとつ増やして、使用時に毎回配列名も指定させます?
使い捨ての名前にそこまで手を煩わせたくないな~
じゃあ、どうするのか?
ここでsysindexを使います。こいつはsasを開いてから閉じるまでの
sasセッションの中で、呼び出される都度+1された連番が入るので
決して重複しないという性質をもっています
以下のようにマクロを書き直しました。
%macro ar2(varlist);
array AR&sysindex $ &varlist;
CN=0;
do over AR&sysindex;
CN + ^missing(AR&sysindex);
end;
drop CN;
%mend ar2;
では再度実行してみます。
data A3;
set Q1;
%ar2(A B C)
count_A_B_C=CN;
%ar2(C D E)
count_C_D_E=CN;
run;
勝手に配列名に連番が付与されていることで、名前の衝突が回避されていることが
わかります。
ちなみに、なぜ5から始まっているかというと
まず最初何もしていない状態が1で
最初データセットA1を作った際に一回%arを呼び出したので
ここで+1されて2になってます。
続いて、結局エラーになりましたがA2で二回呼び出しているので
+1 +1 で4になってます。
だから今回5なんですね。ユーザーが意識しようがしまいが内部的に自動カウント
されているわけです。
SASはfcmpプロシジャの機能がアレなんで、マクロを関数やサブルーチンなどステップ内での
ツール的に使いたいケースも多いんですが、そういう時に知ってると役立ちます