resolve関数の落とし穴
さて、マクロ変数に格納された値を取得したい時、どんな方法があるでしょうか。
通常は、&で展開、symget、resolve関数のいずれかを使うと思いますが、落とし穴があるのでご紹介。
まぁ変なコード例ばかり(こんなコード書かねぇよ、って?書いたのでこの問題に遭遇したのですよ)ではありますけど、知っておけば防げるので…。
%let X=abc;
data _null_;
a=symget('X'); /* symgetでマクロ変数取り出し */
b="&X"; /* &でマクロ変数取り出し */
c=resolve('&X'); /* resolve関数でマクロ変数取り出し */
put a b c;
%let Y=DEF;
a=symget('Y');
b="&Y";
c=resolve('&Y');
put a b c;
call symput('Z','xyz');
a=symget('Z');
b="&Z";
c=resolve('&Z');
put _all_;
put a b c;
run;上記を実行すると、こんな結果になります

1件WARNINGが出ていますね。
実はcall symputで生成したマクロ変数は、dataステップが終了しないと、そのマクロ変数にアクセスできないのです。
(ここは落とし穴でもなんでもなく、)まぁこんなことは皆さんご存知だと思うんですが、symgetとresolveだと、正しく取得できます。コレ結構便利では?
(なんとなくですが、PDV内にマクロ変数用のバッファ領域みたいのがあって、データステップ関数ならそこにアクセスできる、というイメージ。正しいかどうかはわかりませんが)
とまぁ、前置きはこれくらいにして、落とし穴の話を。
以下の2つのマクロを見てください。
%Macro Mtest1(XXX); length tmp $20; tmp='&XXX'; res=resolve(tmp); put res=; %Mend; data _null_; %Mtest1(abc); run; %Macro Mtest2(XXX); data _null_; length tmp $20; tmp='&XXX'; res=resolve(tmp); put res=; run; %Mend; %Mtest2(abc);
一見、マクロ化しているのがdataステップ自体なのか、dataステップ内のステートメントなのかの違いに見えますが、結果に違いが出てきます。
%Mtest1の方では、WARNING: XXXのシンボリック参照を解決できません。という警告が出てしまっています。
マクロパラメータと変数値を結合してマクロ変数にするようなケースでちょっと面倒が発生しそうですが、dataステップ内で展開するマクロ参照を使用する場合には、&で展開してあげれば対応できると思います。
%let name_1_x=mike; data sample; key='x';output; run; %Macro Mgetname(id); res='&&name_&id._'||key; * <-これはNG; res='&name_'||"&id._"||key;* <-マクロを展開しておくことで、resolveにパラメータのマクロ変数を処理させない; name=resolve(res); %Mend; data _null_; set sample; length name $10; %Mgetname(1); put name=; run;
resolve関数は、dataステップ内で展開したマクロ参照のパラメータ値を取得できない。
なんででしょうか?
ちなみに、symgetは以下のような使い方ができます
%let name_1=mike;
%let name_2=alice;
data sample;
key='x';output;output;
run;
%Macro Mgetname(id);
idx=symget("&id");
res='&name_'||idx;
name=resolve(res);
%Mend;
data _null_;
set sample;
length name $10;
call symputx('index',_n_);
%Mgetname(index);
put name=;
run;obs番号である_n_をマクロパラメータとして指定したい場合、そのまま_n_とすると、文字列の「_n_」として処理されてしまうため、一旦call symputxでマクロ変数に格納し、それをマクロ内でsymgetを使って取得、というやり方です。