kickstamp

きりんさんメモと、しゃちくんノート。ときどきウサギちゃんのらくがき

【SAS】PROC FCMPプロシージャーでの可変長引数の利用について

PROC FCMPでは下記のように、引数名[*]と定義し、引数リストの外にvarargsと記述することで可変長引数を扱えます。

__GooglePrettify__
    > options cmplib=sasuser.funcs;
    > 
    > proc fcmp outlib=sasuser.funcs.temp;
    > function summation (b[*]) varargs;
    >     total = 0;
    >     do i = 1 to dim(b);
    >         total = total + b[i];
    >     end;
    > return(total);
    > endsub;
    > sum=summation(1,2,3,4,5);
    >    put sum=;
    > run;

    http://support.sas.com/documentation/cdl/en/proc/61895/HTML/default/viewer.htm#a002975331.htm

ただ一点問題がありdata stepで上記のfunctionを使用する場合は上記のサンプルにあるようにsummation(1, 2, 3, 4, 5)のような記載では使えません。 その理由はdata stepで可変長引数を要求するユーザー定義関数を使用する場合には、引数としてarrray(正確にはtemporary array)を必要とするからです。ただtemporary配列を作成し、変数をそのtemporary配列に格納するのは面倒くさいという問題があります。具体的には下記コード(使用例1)のような作業を必要とします。

__GooglePrettify__
    /* 使用例1 */
    data test;
        numeric1 = 1;
        numeric2 = 2;
        numeric3 = 3;
        numeric4 = 4;
        numeric5 = 5;
        
        array temp[5] _temporary_;              /* temporary配列 */
        array physical[5] numeric1-numeric5;    /* numeric1からnumeric5までを格納した実配列 */
    
        
        do i = 1 to dim(temp);
            /* 実配列からtemporary配列への格納処理 */
            temp[i] = physical[i];
        end;
        result = summation(temp);
        /* result = summation(physical); -> 実配列のためエラーとなる */
        /* result = summation(numeric1, numeric2, numeric3, numeric4, numeric5); -> 配列ではないためエラーとなる */
        put result=;
    run;

上記サンプルの通りdata stepで可変長引数を使用するユーザー定義関数を使用するのはとても面倒くさいです。そのため私は下記のmacroを作成し対処しています。

__GooglePrettify__
    **マクロサンプル;
    /*  summary : 値の入ったtemporary配列を作成する
        args    :
            temporarry_array_name   : temporary配列名称
            length                  : 配列長
            vars                    : 変数(スペース区切りで複数定義)
        example  :
            %create_temporary_array(japanese, 2, word1 word2);
            -> temporary配列であるjapaneseにword1とword2が格納された状態になる
        primary use case :
            可変長引数を求めるユーザー定義関数使用時にtemporary配列を作成する際に使用する
        TODO    :
            lengthをvarsから自動敵に設定したい
                問題点  :
                    1.事前に%count_varsなどで計算した値をmacro変数に格納しても文字列として扱われ、&length.のように扱えない。
    */
    %macro create_temporary_array(temporary_array_name, length, vars);
        %let _local_array_name = __local_&temporary_array_name.;
    
    
        array &temporary_array_name.    [&length.] _temporary_;
        array &_local_array_name.       [&length.] &vars;
    
        %let counter = insert_vars_to_temporary;
        do &counter. = 1 to dim(&_local_array_name.);
            &temporary_array_name.[&counter.] = &_local_array_name.[&counter.];
        end;
    
        drop &counter.;
    %mend;

上記マクロを利用すると先ほどの使用例1のコードがずいぶん省略できたかと思います。

__GooglePrettify__
    data test;
        numeric1 = 1;
        numeric2 = 2;
        numeric3 = 3;
        numeric4 = 4;
        numeric5 = 5;
        
        %create_temporary_array(temp, 5, numeric1-numeric5);
        result = summation(temp);
        put result=;
    run;

ただ、私自身このマクロがいまいち納得のいかない部分もありますので、ユーザー定義関数での可変長引数の使用に対して他にいい方法があったら教えてください。