Specript Language Reference rev.0.7.0 www.specript.org

目次 | << 前章 | 次章 >>

6.function

functionはSpecriptにおいて機能もしくは処理を表すものです。Specriptのfunctionの 本体は、C言語の関数やJavaのメソッドのように命令を順に並べて手続きを示すものではなく、 1つの式として表されます。

6−1.function定義

function定義は、とある式を再利用可能なように定義するものです。定義したfunctionは 所属namespace内および系列下位のnamespaceより、あるいはpublic修飾子が付いていれば 他namespaceから呼び出すことができます。

【function定義の書式(EBNF)】

<function定義> ::=

( "public" | "private" )? "function" <function名> ( "(" ( <引数定義リスト> )? ")" )? ( ":" <functionの返り値のspec> )? "=" ( <ローカルproperty定義リスト> )? <本体式> ";"

<引数定義リスト> ::=

( <引数定義> | <クロージャー引数定義> ) ( "," ( <引数定義> | <クロージャー引数定義> ) )*

<引数定義> ::=

<引数名> ":" ( "not" "null" )? <引数のspec> ( "=" <引数のデフォルト値式> )? ( "#" <制約違反式> )?

<クロージャー引数定義> ::=

<引数名> "(" <引数定義リスト> ")" ":" <引数のspec> ( "=" "|" <引数のデフォルト値式> "|" )?

<ローカルproperty定義リスト> ::=

( <ローカルproperty定義> "," )+

<ローカルproperty定義> ::=

<ローカルproperty名> ( ":" ( "not" "null" )? <ローカルpropertyのspec> )? "=" <初期化式> ( "#" <制約違反式> )?

【記述例】
  • 返り値にspec指定あり
  •     function 肥満度(身長:decimal, 体重:decimal):decimal = 体重 / (身長 * 身長);
        
  • 返り値にspec指定なし
  •     function 肥満度(身長:decimal, 体重:decimal) = 体重 / (身長 * 身長);
        
  • 引数にデフォルト値式あり
  •     function 税込み額(金額:integer, 消費税率:decimal = 0.05):integer = floor(金額 * (1.00 + 消費税率));
        
  • 引数なしのfunction(※"()"を省略)
  •     function 明日の日付:date = Today.NextDate;
        
  • functionローカルなpropertyの使用
  •     function 税込み合計金額(単価:integer, 数量:integer, キャンペーンコード:string):integer =
            小計:integer = 単価 * 数量,
            キャンペーン適用後:integer =
                キャンペーンコード ==?
                    null ? 小計 :
                    "A" ?
                        (小計 >=?
                            10000 ? 小計 * 0.7 :
                             5000 ? 小計 * 0.8 :
                             3000 ? 小計 * 0.9 :
                                    小計
                        ) :
                    "B" ?
                        (小計 >=?
                             8000 ? 小計 * 0.8 :
                             4000 ? 小計 * 0.9 :
                                    小計
                        )
                    ,
            税込み額(金額 = キャンペーン適用後);
        
  • クロージャー引数
  •     // 引数"ff"はクロージャー引数
        function f(ff(a:integer):integer, b:integer):integer = ff(a = 3 + b) + b;
    
        // 呼び出し
        property p = f(ff = |a * 5|, b = 4);
        

    function定義は、予約語"function"に続いて、function名、 "("〜")"内に引数の定義、":"の右に返り値の spec、"="の右辺にfunctionの本体式、と続きます。

    functionの本体式では引数を参照しつつ任意の式を記述できます。この式がfunctionの 機能もしくは処理の内容を表します。

    式の詳細については第8章をご参照ください。

    Specriptのfunctionは、引数を参照しつつ何らかの評価をしてその結果を返す式で表されます。 後述の「functionローカルなproperty」を除いて、functionの本体式中で変数を扱うことは できません。Specriptのfunctionは、原則‘副作用’で処理を進めるものではありません。 引き渡された引数に基づきつつ、なんらかのデータ加工処理を行い、その結果を返却します。 これらの特徴は関数型言語における関数に近いものであると言えます。

    functionのオーバーロード定義、すなわち、同名で異なる引数リストをもつような functionの定義はできません。ただし、引数にデフォルト値式を持たせることが、 オーバーロード定義の代替となる場合があります。

    引数のデフォルト値式については本章第5節を参照してください。

    6−2.返り値のspec

    function定義において、返り値のspecは、指定することも省略することもできます。 specを指定した場合、function本体式の評価結果が指定されたspecと互換性がないとき、 コンパイル時エラーまたは実行時エラーとなります。

    【例】
        // 返り値のspecが本体式の評価結果と互換性がありません。
        function foo(x:decimal, y:decimal):integer = x + y;  // コンパイル時エラー
        
    specの互換性評価の詳細について第7章第3節を参照してください。

    返り値のspec指定を省略した場合、functionの返り値のspecは、簡単な型推論により、 本体式の評価結果が表すspecであるとされます。

    【例】
        // 返り値のspecは暗黙にdecimalであるとされます。
        function foo(x:decimal, y:decimal) = x + y;
        

    6−3.引数のspec

    functionの各引数の引数名の右に、":"で区切ってその引数のspecを指定する ことができます。一方、引数のspecを省略することはできません。

    6−4.function呼び出し時の記法

    Specriptにおけるfunction呼び出しは、下記例のように引数名を明示して、引き渡す値を 表す式を"="の右辺に記述します。このとき引数の順番は任意となります。

    【例】
        function 税込み額(金額:integer, 消費税率:decimal):integer = floor(金額 * (1.00 + 消費税率));
    
        // 各引数名を明示し、引き渡す値を右辺に記述します。
        property a = 税込み額(金額 = 100, 消費税率 = 0.05);
    
        // 任意の順序で引数を記述できます。
        property a = 税込み額(消費税率 = 0.05, 金額 = 100);
        

    引数名を明示しない記法は許されません。

    【例】
        property a = 税込み額(100, 0.05);  // コンパイル時エラー
        

    ただし、引数が1個のfunctionに限って、引数名を省略することができます。

    【例】
        // 引数が1個であるfunctionの定義です。
        function foo(a:integer):integer = ...;
    
        // 引数1個のfunctionの呼び出しに限り引数名を省略できます。
        property a = foo(12);  // OK
        

    6−5.引数にデフォルト値式のあるfunction

    functionの引数にはデフォルト値式を指定することができます。デフォルト値式の 指定は、下記例のように、function定義において、引数の右に"="を置いて、 その右辺にデフォルト値を表す式を記述することで行います。デフォルト値式のある引数は、 function呼び出し時に省略することができ、省略された場合、デフォルト値式の評価値が 引数の値として採用されます。

    【例】
        // 引数"消費税率"にデフォルト値式が指定されています。
        function 税込み額(金額:integer, 消費税率:decimal = 0.05):integer = floor(金額 * (1.00 + 消費税率));
    
        // function呼び出し時、引数"消費税率"が明示的に指定されています。
        property a = 税込み額(金額 = 100, 消費税率 = 0.07);  // a == 107となります。
    
        // function呼び出し時、引数"消費税率"が指定されていません。デフォルト値式の
        // 評価結果である"0.05"を用いて計算されます。
        property b = 税込み額(金額 = 100);  // b == 105となります。
        

    デフォルト値式には、リテラルだけでなく、任意の式が記述できます。

    デフォルト値式は、functionの引数定義リストの最後以外の引数にも指定できます。 呼び出し時は、省略しない引数を任意の順序で列挙すればよいこととなります。

    【例】
        // 先頭の"a"にデフォルト値式あり
        function f1(a:integer = 1, b:integer, c:integer):integer = ... ;
    
        // 呼び出し時、"a"を省略
        property p1 = f1(b = 2, c = 3);
    
        // 中間の"b"にデフォルト値式あり
        function f2(a:integer, b:integer = 1, c:integer):integer = ... ;
    
        // 呼び出し時、"b"を省略、引数の順序は任意
        property p2 = f1(c = 3, a = 1);
    
        // 飛び飛びで"a"、"c"にデフォルト値式あり
        function f3(a:integer = 1, b:integer, c:integer = 1):integer = ... ;
    
        // 呼び出し時、"b"のみ指定
        property p3 = f3(b = 2);
        

    全ての引数にデフォルト値式が存在していて、呼び出し時に全ての引数を省略する場合、 "()"だけを記述します。(後述の引数なしfunctionとは異なり "()"は必須です。)

        // 全ての引数に指定されていてもOK
        function f4(a:integer = 1, b:integer = 1, c:integer = 1):integer = ... ;
    
        // 呼び出し時、全ての引数を省略、( ) は必須
        property p4 = f4();
        

    前述のように、Specriptではfunctionのオーバーロード定義、すなわち、同名で異なる 引数リストをもつようなfunctionの定義はできません。引数にデフォルト値式を持たせることが、 オーバーロード定義の代替となる場合があります。

    【例】
        // 引数が1個と2個の同名functionを定義するとコンパイルエラーとなります。
        function 税込み額(金額:integer):integer = ...;  // コンパイルエラー
        function 税込み額(金額:integer, 消費税率:decimal):integer = ...;  // コンパイルエラー
    
        // 上記のような場合は、デフォルト値式を用いると同じ効果を得られます。
        function 税込み額(金額:integer, 消費税率:decimal = 0.05):integer = ...;  // OK
        

    6−6.引数なしのfunction

    function定義において引数が一つも無いfunctionを定義できます。このとき、引数定義リストの "()"を省略することができます。引数なしのfunctionは、呼び出し時にも "()"を省略することができます。

    【例】
        // 引数なしのfunctionの定義、( )有り
        function f1():integer = 60 * 60 * 24;  // OK
    
        // 引数なしのfunctionの定義、( )無し
        function f2:integer = 60 * 60 * 24;  // OK
    
        // 呼び出し時、( )有り
        property p = foo() + bar();  // OK
    
        // 呼び出し時、( )無し
        property q = foo + bar;  // OK
        

    引数なしfunctionで"()"を省略すると、propertyと見た目の区別がありません。

    ここで、引数なしfunctionで"()"を省略したものとpropertyの差異を説明します。

    【例】
        function 1日の秒数:integer = 60 * 60 * 24;
    
        property 1日の秒数:integer = 60 * 60 * 24;
    
        function 明日の日付:date = Today.NextDate;
    
        property 明日の日付:date = Today.NextDate;
        

    propertyの場合、初期化式はproperty定義時(一般にはアプリケーション起動時)に一度だけ 評価されます。引数なしfunctionの場合、本体式はfunction呼び出しの都度評価されます。 上記の例の場合、"1日の秒数"は、一度評価されれば十分なのでpropertyの 方が望ましいといえます。一方、"明日の日付"はpropertyにすると アプリケーション起動時のシステム日付に基いて評価された後、二度と再評価されません。 functionであれば、呼び出しの都度評価されます。"明日の日付"の場合は functionの方が望ましいといえます。

    もう一点の重要な違いは、特にspecの要素としてpropertyやfunctionを定義するとき、 propertyとして定義された要素はJava側と交換されますが、functionとして定義された 要素はJava側と交換されません。

    spec定義については第7章第1節を参照してださい。

    6−7.functionローカルなpropertyの定義

    function定義において、そのfunctionローカルに有効なpropertyを定義できます。 functionローカルなpropertyは、function本体式を評価することに先立って初期化され、 本体式中で参照することができます。ローカルproperty定義は、function定義の "="の右側、本体式の前に、本体式とは","で区切って記述します。

    functionローカルなpropertyは、定義されるfunction内でのみ参照可能です。他のfunctionの 本体式等より参照することはできません。

    functionローカルなpropertyの初期化式内では、functionの引数を参照することができます。

    ひとつのfunction定義内にて、複数のローカルpropertyを定義可能です。このとき、定義が 先行するローカルpropertyを、定義が後続するローカルpropertyの初期化式内で参照可能です。 後続定義されるローカルpropertyを、先行して定義されるローカルpropertyの初期化式内で 参照することはできません。

    【例】
        function f1(a:integer, b:integer, c:integer):integer =
            lp1:integer = a + b,    // ローカルproperty定義の初期化中で引数を参照できます。
            lp2:integer = lp1 - c,  // 先行定義されるローカルproperty "lp1"を参照できます。
            (lp1 + lp2) * 2;        // 本体式中でローカルpropertyを参照できます。
    
        // 後続定義されるローカルpropertyは参照できません。
        function f2(a:integer, b:integer, c:integer):integer =
            lp1:integer = lp2 - c,  // コンパイル時エラー
            lp2:integer = a + b,
            (lp1 + lp2) * 2;
        

    定義するローカルpropertyの名称は、そのfunctionの引数名と重複させることはできません。

    【例】
        // ローカルproperty "y"が引数"y"と衝突しています。
        function f(x:integer, y:integer):integer =
            y:integer = x * 2,  // コンパイル時エラー
            z:integer = y * 2,
            x + y + z;
        

    ローカルpropertyは、上記の各事項以外は、一般のpropertyと全く同じように定義したり 参照したりすることができます。

    6−8.クロージャー引数

    function定義において、その引数をクロージャーとすることができます。クロージャーを 引数にする場合、引数定義の記述において‘引数のある引数’として定義します。

    クロージャー引数をfunction本体式中、もしくはローカルpropertyの初期化式中で参照する ときは、function呼び出しの形式を取ります。

    【例】
        // 引数"ff"はクロージャー引数、‘引数のある引数’として定義しています。
        function f(ff(a:integer):integer, b:integer):integer =
            ff(a = 3 + b) + b;  // クロージャー引数"ff"の引数"a"に"3 + b"の評価結果を引き渡しつつ、
                                // "ff"を呼び出します。
        

    クロージャー引数をとるfunctionを呼び出すとき、クロージャーは"|"で 括った「引用式」として引き渡します。

    【例】
        // 引数"ff"はクロージャー引数
        function f(ff(a:integer):integer, b:integer):integer = ... ;
    
        // 呼び出し時、"|"で括った引用式でクロージャーを記述します。
        property p = f(ff = |a * 5|, b = 4);
        

    引用式中では、‘クロージャー引数の引数’を参照するような式を記述します。上記例では、 function "f"呼び出し時において、クロージャー引数"ff"に 対応する引用式として、クロージャー引数の引数"a"を参照した式"a * 5"を 記述しています。

    クロージャー引数へ引き渡すクロージャーは必ず"|"で括った「引用式」とする 必要があります。クロージャー引数へ引用式でない式を引き渡すとコンパイル時エラーと なります。一般の引数に引用式を引き渡してもコンパイル時エラーとなります。

    クロージャー引数を「引数なし」にすることはできません。

    ‘クロージャー引数の引数’にもデフォルト値式を記述できます。

    クロージャー引数をとるfunction呼び出し時にも、引数名を明示して、任意の順序で引数を 記述できます。

    【例】
        // 引数"ff"はクロージャー引数
        function f(ff(a:integer):integer, b:integer):integer = ... ;
    
        // 呼び出し時、引数名を明示し、順番は任意です。
        property p = f(b = 3, ff = |a * 5|);
        

    定義済みの任意のfunctionを直接クロージャーとして引き渡すことはできません。 そのfunctionを呼び出すような引用式を記述する必要があります。

    【例】
        // 引数"ff"はクロージャー
        function f(ff(a:integer):integer, b:integer):integer = ... ;
    
        // 定義済みfunction "myff"
        function myff(x:integer):integer = ... ;
    
        // 呼び出し時、定義済みfunctionを直接引き渡すことはできません。
        property p = f(ff = myff, b = 3);  // コンパイル時エラー
    
        // 定義済みfunctionを呼び出すような引用式を記述してください。
        property q = f(ff = |myff(x = a)|, b = 3);  // OK
        

    目次 | << 前章 | 次章 >>


    (c)2007-2008, Specript Development Team Last Updated: 2008-05-27