ラベル CALL SYMPUT の投稿を表示しています。 すべての投稿を表示
ラベル CALL SYMPUT の投稿を表示しています。 すべての投稿を表示

2017年8月10日木曜日

そのマクロ変数、ローカル?グローバル?③CALL SYMPUT編



「CALL SYMPUT」 または 「CALL SYMPUTX」 で定義したマクロ変数が、ローカル・グローバルどちらになるのか解説します。


まずは前提知識。




シンボルテーブル


マクロ変数を入れる箱の事をシンボルテーブルといいます。

・ローカルシンボルテーブル ・・・ ローカルマクロ変数を入れる箱
・グローバルシンボルテーブル ・・・ グローバルマクロ変数を入れる箱

図1
つまりマクロ変数がローカルなのか?グローバルなのか?という問題は、どちらのシンボルテーブルに入るかを問題としているわけです。




CALL SYMPUT で定義したマクロ変数の、ローカル・グローバルの判定方法


📝 説明の前に・・
SAS社のリファレンスの説明と実際の動作に差異があったため、私の実験結果に基づく動作の説明も含みます。何か不足している点など、お気づきの点がありましたらコメントいただけると幸いです。



CALL SYMPUT が含まれるデータステップの「run;」がマクロの中にあるのか、マクロの外にあるのかで、ローカル・グローバルの判定結果が変わります。


マクロの中に「run;」がある
%macro TEST;
   data _null_;
      call symput("MAC1", "1234");
   run;
%mend;
%TEST;

マクロ外に「run;」がある
data _null_;
   call symput("MAC1", "1234");
run;



判定は以下のとおり。
図2



具体例


では具体例をみていきましょう。
まず今回の解説で使うマクロ変数を予めリセット(削除)しときます。

%symdel MAC1 MAC2 MAC3 MAC4 MAC5 MAC6 MAC7 MAC8 MAC9 MAC10 / nowarn;



① マクロ外で定義したマクロ変数が、「グローバル」になる例。

data _null_;
   call symput("MAC1","abcd");
run;
%put _user_;

ログ
GLOBAL MAC1 abcd


② マクロ内で定義したマクロ変数が、「グローバル」になる例。

%macro TEST;
    data _null_;
       call symput("MAC2","abcd");
    run;

    %put _user_;
%mend;
%TEST;

ログ
GLOBAL MAC2 abcd


③ マクロ内で定義したマクロ変数が、「ローカル」になる例。

%macro TEST;
    %let MAC3 = abcd;

    data _null_;
       call symput("MAC4","1234");
    run;

    %put _user_;
%mend;
%TEST;

ログ
TEST MAC3 abcd
TEST MAC4 1234



例外

上のほうで示した「図2」のオレンジ色で示した「例外あり」のルートでは以下の例外が起きます。

① マクロ内で「SQLプロシジャ」の後に実行するCALL SYMPUTのマクロ変数は「ローカル」になる。

%macro TEST;
    proc sql ;
       select * from SASHELP.CLASS;
    quit;

    data _null_;
       call symput("MAC5","abcd");
    run;

    %put _user_;
%mend;
%TEST;

ログ
TEST MAC5 abcd


② マクロ内で「%GOTO &マクロ変数」や「%GOTO %マクロ名」みたいな記述があると、その後のCALL SYMPUTのマクロ変数は「ローカル」になる。

%let MAC6=GOGO;

%macro TEST;
    %goto &MAC6;
    %GOGO:

    data _null_;
       call symput("MAC7","1234");
    run;

    %put _user_;
%mend;
%TEST;

ログ
TEST MAC7 1234
GLOBAL MAC6 GOGO



③ マクロで「parmbuff」を使っていると、CALL SYMPUTのマクロ変数は「ローカル」になる。

%macro TEST / parmbuff;
    data _null_;
       call symput("MAC8","1234");
    run;

    %put _user_;
%mend;
%TEST;

ログ
TEST MAC8 1234



失敗例


① たまに質問を頂くので注意喚起。
CALL SYMPUTで定義したマクロ変数を同じデータステップ内で参照することは出来ません。

data _null_;
   call symput("MAC9","123");
   x = &MAC9;
run;

ログ
 WARNING: MAC9のシンボリック参照を解決できません。




② これは上級者が遭遇する失敗例。
「CALL SYMPUTでマクロ変数を定義」&「そのマクロ変数を使用(参照)する」みたいなマクロがあったとして、そのマクロをCALL EXECUTEで実行すると、うまくマクロ変数の参照が出来ないようです。

%macro TEST;
   data _null_;
      call symput("MAC10","123");
   run;

   %put &MAC10;
%mend;

data _null_;
   call execute('%TEST');
run;

ログ
 WARNING: MAC10のシンボリック参照を解決できません。

SAS社のUsage Noteにも載ってました。



てことで、どうでしょう、、正直最初はなんじゃこりゃーって感じでいろんなシチュエーションがあって覚えらんないですよね。慣れれば、なんとなくわかってきます。




記事一覧

1. 基本概念
2. %LET編
3. CALL SYMPUT編
4. マクロ変数の展開
5. マクロ変数の作成場所を明示的に指定する

2014年7月19日土曜日

値をマクロ変数に格納する「CALL SYMPUTX」その2




前回の続きです。ちょっとした落とし穴を紹介。


まず以下の例を見てみましょう。

* マクロ変数を削除 ;
%symdel X  / nowarn;

* call symputxでマクロ変数を作りログに出力 ;
%macro TEST;
     data _null_;
         call symputx("X", "abcd");
     run;
%mend;
%TEST;

%put "&X";

ログ
"abcd"



ログを見てみると、問題なくマクロ変数を参照できてますね。
では次のプログラムはどうでしょうか?

* マクロ変数を削除 ;
%symdel X2 X3 / nowarn;

* call symputxでマクロ変数を作りログに出力 ;
%macro TEST2;
     %let X2=1;
     data _null_;
         call symputx("X3", "abcd");
     run;
%mend;
%TEST2;

%put "&X3";

ログ
WARNING: 記号参照X3を展開していません。


WARNINGが出て参照できませんでした。
最初のマクロとの違いと言えば「%let X2=1;」が追加されたくらいですが、実はこれが影響してます。


超ざっくり説明すると、「%let X2=1;」はマクロTEST2の中で定義してるマクロ変数で、いわゆるローカルマクロ変数(以下参照)です。

  ローカルマクロ変数:
   そのマクロ内でしか参照できないマクロ変数(マクロが実行終了すると消去される)

  グローバルマクロ変数:
   どの場所からでも参照できるマクロ変数


マクロの中でひとつでもローカルマクロ変数があると、同マクロ内に書かれたCALL SYMPUTXの結果もローカルマクロ変数となります。(グローバルマクロ変数になる場合もある。詳細はこちらを参照)





もしグローバルマクロ変数に変えたい場合、はじめに「%GLOBAL マクロ変数名;」を追記すればOK。
逆にローカルマクロ変数にしたい場合は、%LOCALステートメントを追記します。

またSYMPUTにはなくSYMPUTXのみの機能として以下のように書けちゃいます。

  call symputx("X3", "abcd" , "G") ;

  "G" …  グローバルマクロ変数にする。
  "L"  …  ローカルマクロ変数にする(マクロ外で実行するとグローバルマクロ変数になる)


この点もCALL SYMPUTXを使うメリットだと思います。


2014年7月18日金曜日

値をマクロ変数に格納する「CALL SYMPUTX」その1




CALL SYMPUTは知ってるけど、CALL SYMPUTXって何?という人も多いかと思います。


共に変数値や値をマクロ変数に格納することが出来ます。
ではどう違うのかというと、、


例1: 文字値をマクロ変数に格納する
data _null_;
   length X $10.;

   X = "abcd";
   call symput("MAC1", X);
   call symputx("MAC2", X);
run;

%put "&MAC1";
%put "&MAC2";

ログ
"abcd      "
"abcd"

変数Xの値をマクロ変数MAC1とMAC2に入れてから%PUTでログにその値を展開してます。
  • CALL SYMPUTはLENGTHの余りを半角スペースとして保持してしまうのに対して、
  • CALL SYMPUTXは両端の半角スペースを取り除いてくれます。


例2: 数値をマクロ変数に格納する
data _null_;
   Y = 123.456;
   call symput("MAC3", Y);
   call symputx("MAC4", Y);
run;

%put "&MAC3";
%put "&MAC4";

ログ
"     123.456"
"123.456"

  • CALL SYMPUTでは数値を文字変換してマクロ変数に格納するので、ログに「NOTE: 以下の箇所で数値を文字値に変換しました」と出力されます。またマクロ変数値の先頭に半角スペースが入ります。
  • CALL SYMPUTXでは文字変換&両端の半角スペースを取り除いて、かつログには文字変換した旨のメッセージは出力されません。


例3: 数値をマクロ変数に格納する際の注意
data _null_;
   Y = 111.111111112;
   call symput("MAC5", Y);
   call symputx("MAC6", Y);
run;

%put "&MAC5";
%put "&MAC6";

ログ
"111.11111111"
"111.11111111"

マクロ変数に格納したら、数値が丸められてしまいました。
小数部分の桁が多くて勝手な丸めがまずい場合は、自分で適切な文字値に変換した値をマクロ変数に格納すると安全です。


次回に続く。