You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

[ |../../index.htm#lansa/subroutine_e_p2.htm]
現在地:

7.121.4 SUBROUTINE の使用例 - 2

BBUSEテンプレートを使用したSUBROUTINEのドキュメント化技法
再帰
サブルーチン変数はローカルでスコープ指定されない
命名標準を使用してローカル・スコープ指定をエミュレートする
グローバルにスコープ指定された変数の保管/復元技法
7.121.3 SUBROUTINE の使用例 - 1
BBUSEテンプレートを使用したSUBROUTINEのドキュメント化技法
SUBROUTINEコマンドを使用するときは、一般に、BBSUBというLANSAテンプレート (Visual LANSAおよびLANSA/AD用) を使用することをお勧めします。
このテンプレートでは、以下のようなサブルーチンの基本コーディング・レイアウトが提供されます。
*=============================================================================
*Subroutine ....:
*Description....:
*=============================================================================
SUBROUTINE NAME(SUB1)
ENDROUTINE
サブルーチンにパラメータが必要な場合は、このテンプレートにより、以下のような基本コーディング・レイアウトが自動的に提供されます。
*=============================================================================
*Subroutine ....:
*Description....:
*Parameters ....:   Name  Type  Len  Description

            • ------- ------- -------- -------------
              *#XXXXXX    XXX  99,9 XXXXXXXXXXXXXXXXXXXX
                  *=============================================================================
              SUBROUTINE NAME(SUB1) PARMS(#XXXX)
              ENDROUTINE
              例えば、SUB1というサブルーチンでパラメータ#EMPNO、#SURNAME、および#GIVENAMEが使用されている場合、これらのパラメータを、BBUSEテンプレートで作成される基本レイアウトでコメントにすることにより、サブルーチンをわかりやすく、また将来的に実装しやすくすることができます。
              すなわち、レイアウトには、以下のように、サブルーチンでパラメータとして使用されるフィールドについての情報を含めることができます。
              *=============================================================================
              *Subroutine....:SUB1
              *Description....: To retrieve an employee record from file PSLMST
              *Parameters ....: #EMPNO, #SURNAME and #GIVENAME 
              *Name        Type      Len      Description      
              *-----       -------   -------  -----------------------------
              *#EMPNO1     A         5        Employee number
              *#NAME1      A         20       Surname
              *#NAME2      A         20       Givename
                  *==========================================================================
              SUBROUTINE
              NAME(SUB1) PARMS((#EMPNO1 *RETURNED) (#SURNAME *RETURNED) (#GIVENAME *RETURNED))
              ENDROUTINE
              再帰
              直接または間接を問わず、SUBROUTINEを再帰的に呼び出すことは避けてください。
              以下の例では、SUBROUTINE SUB_Aがそれ自体によって再帰的に呼び出されます。
              SUBROUTINE SUB_A << ETC >> EXECUTE SUB_A << ETC >> ENDROUTINE    
              この例では、Visual LANSAでは致命的エラーが発生し、LANSA/ADでは単にコンパイルに失敗します。
              以下の例では、SUBROUTINE SUB_AがSUBROUTINE SUB_Bによって再帰的に呼び出されます。
              SUBROUTINE SUB_A << ETC >> EXECUTE SUB_B << ETC >> ENDROUTINE     SUBROUTINE SUB_B << ETC >> EXECUTE SUB_A << ETC >> ENDROUTINE    
              この例でも、"Visual LANSA"では致命的エラーが発生しますが、LANSA/ADでは、手作業での終了が必要な再帰的ループになります。
              一方、RDMLXのサブルーチンMTHROUTINE (メソッド・ルーチン)では再帰が許容されるため、以下の階乗計算は正しく機能します。
              Mthroutine Factorial Define_Map input #Std_Num #OfNumber Define_Map *output #Std_Num #ReturnResult If        '#OfNumber.Value = 1' Set       #ReturnResult Value(1) Else       Change    #Std_Num '#OfNumber.Value - 1' Invoke    #Com_Owner.Factorial OfNumber(#Std_Num) ReturnResult(#ReturnResult) Change    #Std_NumL '#OfNumber.Value  #ReturnResult.Value' Set       #ReturnResult Value(#Std_Num) Endif      Endroutine  
              そのため、Number(4) の Invoke #Com_Owner.factorial で ReturnResult(#Std_Num)により、4 * 3 * 2 *1 = 24という結果が返されます。
              サブルーチン変数はローカルでスコープ指定されない
              一般に、サブルーチンが受け取り、サブルーチンによって返される引数は、以下のようにそのサブルーチン内で定義します。
              SUBROUTINE NAME(A) PARMS(#A #B #C) DEFINE    FIELD(#A) REFFLD(#SALARY) DEFINE    FIELD(#B) REFFLD(#PERCENT) DEFINE    FIELD(#C) REFFLD(#SALARY)  
              RDMLでは、このようなフィールド定義は単なる慣習的なものです。これらのフィールドはローカルでスコープ指定されず、RDMLファンクション内でグローバルにスコープ指定されます(すなわち、ファンクション内のすべてのコードからアクセスできます)。
              そのため、このコードに含まれる以下の例では、SUBROUTINE SUB_Aによって返される値は、値17.72ではなく42.45になります。
              FUNCTION  OPTIONS(*DIRECT) Define    Field(#newsal) Reffld(#salary) Execute   Subroutine(SUB_A) With_Parms(#newsal) Display   Fields(#newsal)           Subroutine SUB_A ((#A *returned)) Define    #A reffld(#salary) Change    #A 17.72 Execute   SUB_B Endroutine            Subroutine SUB_B Change     #A 42.45 Endroutine  
              これは、#Aがグローバルにスコープ指定されるためです。すなわち、コード内で#Aを参照するときは、常に#Aの同じインスタンスを参照することになります。
              一方、RDMLXでは、EVTROUTINE、MTHROUTINE、およびPTYROUTINEでローカル・スコープ指定がサポートされます。
              上記のSUB_AおよびSUB_Bサブルーチンを以下のようにメソッドとしてコーディングしたとします。
              Function  Options(*Direct) Begin_Com Role(*Extends #Prim_Form) Define_Com Class(#Salary.Visual) Name(#Salary) DisplayPosition(1) Height(19) Left(43) Parent(#Com_Owner) TabPosition(1) Top(72) Width(278)           Evtroutine handling(#com_owner.Initialize) Set       #com_owner caption(*component_desc) Invoke    #com_owner.SUB_A A(#Salary) Endroutine            Mthroutine SUB_A Define_Map *output #Salary #A   Set       #A Value(17.72) Invoke    #com_owner.SUB_B Endroutine            Mthroutine SUB_B Define_Com Class(#Salary) Name(#A)   Set       #A Value(42.45) Endroutine End_Com  
              この場合、メソッドSUB_Aにより17.72が返されます。
              これは、ローカルでスコープ指定された2つの#Aが定義されるためです。
              1つはメソッドSUB_Aで、もう1つはメソッドSUB_Bで定義されます。
              ただし、コードを以下のように定義したとします。
              Define_Com Class(#Salary) Name(#A)           Mthroutine SUB_A Define_Map *output #Salary #ReturnValue   Set       #A Value(17.72) Invoke    #com_owner.SUB_B Set       #ReturnValue Value(#A) Endroutine            Mthroutine SUB_B Set       #A Value(42.45) Endroutine  
              #Aはグローバルにスコープ指定されたコンポーネントであるため、この場合もメソッドSUB_Aにより42.45が返されます。すなわち、SUB_AおよびSUB_Bの両方が同じ#Aを参照します。
              命名標準を使用してローカル・スコープ指定をエミュレートする
              サブルーチンでは、ローカルでスコープ指定された変数がサポートされませんが、単純な命名標準を使用することにより、(必要に応じて)このような変数をエミュレートすることができます。
              例えば、各サブルーチンでは、その引数および変数が一意に定義されます。
              FUNCTION  OPTIONS(DIRECT) DEFINE    FIELD(#PERCENT) TYPE(*DEC) LENGTH(4) DECIMALS(1) DESC(PERCENTAGE) EDIT_CODE(3) REQUEST   FIELDS(#EMPNO #PERCENT) FETCH     FIELDS(#SALARY) FROM_FILE(PSLMST) WITH_KEY(#EMPNO) EXECUTE   SUBROUTINE(SUB_A) WITH_PARMS(#SALARY #PERCENT #EMPNO) *          SUBROUTINE NAME(SUB_A) PARMS((#A_001 *Received)(#B_001 *received)(#C_001 *Received)) DEFINE    FIELD(#A_001) REFFLD(#SALARY) DEFINE    FIELD(#B_001) REFFLD(#PERCENT) DEFINE    FIELD(#C_001) REFFLD(#EMPNO) CHANGE    FIELD(#A_001) TO('#A_001  #B_001') DISPLAY   FIELDS(#C_001 #A_001) EXECUTE   SUB_B (#A_001 #B_001 #C_001)  ENDROUTINE  *          SUBROUTINE NAME(SUB_B) PARMS((#A_002 *Received)(#B_002 *received)(#C_002 *Received)) DEFINE    FIELD(#A_002) REFFLD(#SALARY) DEFINE    FIELD(#B_002) REFFLD(#PERCENT) DEFINE    FIELD(#C_002) REFFLD(#EMPNO) CHANGE    FIELD(#A_002) TO('#A_002  500') DISPLAY   FIELDS(#C_002 #A_002) EXECUTE   SUBROUTINE(SUB_C) WITH_PARMS(#A_002 #C_002) ENDROUTINE  *          SUBROUTINE NAME(SUB_C) PARMS((#A_003 *received)(#C_003 *Received)) DEFINE    FIELD(#A_003) REFFLD(#PERCENT) DEFINE    FIELD(#C_003) REFFLD(#EMPNO) CHANGE    FIELD(#A_003) TO('#A_003  100') DISPLAY   FIELDS(#EMPNO #A_003) ENDROUTINE  
              Execute SUB_A (#Salary #Percent #Empno)をコーディングした場合、さまざまなサブルーチン間で受け渡しされるこれらの値が、グローバルにスコープ指定された値#Salary #Percent #Empnoに誤って干渉することはありません。
              グローバルにスコープ指定された変数の保管/復元技法
              上記の例では、ローカルでスコープ指定された変数をエミュレートする簡単な方法を示しました。
              サブルーチンを作成する際に、グローバルにスコープ指定された変数の上書きを避けることができない場合もあります(ファイルからフィールドを取得する必要がある場合など)。
              同様に、サブルーチンの保守を行う際に、詳細な検査を行うことなく、プログラム内の他の場所でグローバルにスコープ指定された変数がどのように使用されているかについて常に確信を持てるわけではありません。
              このような状況では、通常、単純な保管/復元技法を使用することによって、グローバルにスコープ指定された変数がサブルーチンの実行によって変更されないようにすることができます。
              例えば、ロジック内で、従業員が所属する部門(#DEPTMEN)を取得するSUB_Aというサブルーチンを作成する必要がある場合について考えます。
              Subroutine Name(SUB_A) Parms((#A_001 *received)) Define    #A_001 Reffld(#Empno)   *<<etc>>    Fetch     (#Deptment) from_file(pslmst) with_key(#A_001) *<< etc >>  Endroutine  
              ここで、フィールド#DEPTMENTが、すでにプログラムの複数の場所で使用されているとします。
              新しいサブルーチンで、フィールド#DEPTMENTの値について混乱しないためには、以下のようにコーディングします。
              Subroutine Name(SUB_A) Parms((#A_001 *received)) Define    #A_001 Reffld(#Empno)   Define    #Save_001 Reffld(#Deptment)   *<< etc >>  Change    #Save_001 #Deptment *<< etc >>  Fetch     (#Deptment) from_file(pslmst) with_key(#A_001) *<< etc >>  Change    #Deptment #Save_001 *<< etc >>  Endroutine  
              これにより、#DEPTMENTの値がサブルーチンの実行によって変更されることはありません。
              ここで、#SECTION、#SURNAME、および#STARTDTEも参照しなければならないとします。
              この場合、サブルーチンは以下のようになります。
              Subroutine Name(SUB_A) Parms((#A_001 *received)) Define    #A_001 Reffld(#Empno)   Define    #SavA_001 Reffld(#Deptment)   Define    #SavB_001 Reffld(#Section)   Define    #SavC_001 Reffld(#Surname)   Define    #SavD_001 Reffld(#Startdte)   *<< etc >>  Change    #SavA_001 #Deptment Change    #SavB_001 #Section Change    #SavC_001 #Surname Change    #SavD_001 #Startdte *<< etc >>  Fetch     (#Deptment) from_file(pslmst) with_key(#A_001) *<< etc >>  Change    #Deptment #SavA_001 Change    #Section #SavB_001  Change    #Surname #SavC_001  Change    #Startdte #SavD_001  *<< etc >>  Endroutine  
              ただし、単純な作業リストを使用すれば、より効率的でわかりやすく、保守しやすい方法で、同じ結果を得ることができます。
              Subroutine Name(SUB_A) Parms((#A_001 *received)) Define    #A_001 Reffld(#Empno)   
              このサブルーチンによって上書きされている可能性のあるグローバル・フィールドは以下のとおりです。
              Def_List  #Save_001 (#Deptment #Section #Surname #StartDte) Type(*Working) Entrys(1)  
              上書きされる可能性ある、グローバルに定義されているすべてのフィールドの値を保存します。
              Inz_List  #Save_001 Num_Entrys(1) *<< etc >>  Fetch     (#Deptment #Section #StartDte #Surname) from_file(pslmst) with_key(#A_001) *<< etc >>  
              上書きされている可能性ある、グローバルに定義されているすべてのフィールドの値を復元します。
              Get_Entry 1 #Save_001 Endroutine  
              [ |../../index.htm#lansa/subroutine_e_p2.htm]
  • No labels