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を使って取得、というやり方です。