配列として使用
関数での引数および変数の操作
推奨する引数ネーミングスタイル
引数には、その用途がわかる名前を付けるようにしてください。例えば、次のコードで混乱が発生することはまずありません。
<cfscript> function SumN(Addend1,Addend2) { return Addend1 + Addend2; } </cfscript> <cfset x = 10> <cfset y = 12> <cfoutput>#SumN(x,y)#</cfoutput>
これに似た次のコードでは、プログラミングエラーが発生する可能性が高くなります。
<cfscript> function SumN(x,y) { return x + y; } </cfscript> <cfset x = 10> <cfset y = 12> <cfoutput>#SumN(x,y)#<cfoutput>
引数の受け渡し
次のデータ型は、その値が関数に渡されます。
- 整数
- 実数
- 文字列(リストを含む)
- 日付時刻オブジェクト
- 配列
したがって、関数の定義とそれを呼び出すコードが同じ ColdFusion ページにある場合でも、関数内で引数が変更されたことで、関数呼び出しに使用した変数が変更されることはありません。
クエリー、構造体、COM オブジェクトなどの外部オブジェクトは、その参照が関数に渡されます。したがって、関数内でこれらの引数が変更されると、呼び出しコード内の変数の値も変更されます。
引数の受け渡しの影響の例については、Passing complex dataを参照してください。 ( 複合データ型の 処理の中)を参照してください。
複合データの受け渡し
構造体、クエリ、および COM オブジェクトなどの複合オブジェクトは、その参照がユーザー定義関数に渡されます。したがって、呼び出し側と同じデータがユーザー定義関数でも使用されます。配列は、その値がユーザー定義関数に渡されます。ユーザー定義関数は配列データの新しいコピーを受け取るので、呼び出し元ページの配列がユーザー定義関数によって変更されることはありません。配列は、他の複合データ型と異なる処理が必要です。
ColdFusion(2021 リリース)では、配列を参照で渡すこともできます。Application.cfc で変数 this.passArrayByReference を true に設定します。
構造体、クエリ、およびオブジェクトの受け渡し
呼び出し側の構造体、クエリ、またはオブジェクトを関数で変更するには、その変数を引数として渡します。関数は呼び出し側の構造体への参照を取得するので、関数でその構造体に加えた変更は、すべて呼び出し側の変数に反映されます。呼び出し側にその構造体を返す必要はありません。関数から戻った呼び出し元ページでは、 関数に渡した構造体変数を使用して、 変更されたデータにアクセスできます。
呼び出し側の構造体、クエリ、またはオブジェクトが関数で変更されないようにしたい場合は、Duplicate 関数を使用してその複製を作成してから、それを関数に渡します。
配列の受け渡し
呼び出し側の配列を関数で変更したい場合は、関数で配列を受け取って変更し、それを関数の return ステートメントで呼び出し側に返すのが最も簡単です。呼び出し側では、引数として渡した変数に戻り値を代入します。
配列を直接渡して返す方法を次の例に示します。この例の doubleOneDArray 関数は、1 次元配列の各要素の値を 2 倍にします。
<cfscript> //Initialize some variables //This creates a simple array. a=ArrayNew(1); a[1]=2; a[2]=22; //Define the function. function doubleOneDArray(OneDArray) { var i = 0; for ( i = 1; i LE arrayLen(OneDArray); i = i + 1) { OneDArray[i] = OneDArray[i] * 2; } return OneDArray; } //Call the function. a = doubleOneDArray(a); </cfscript> <cfdump var="#a#">
この解決方法は簡単ですが、適切でない場合もあります。
- この方法では、配列全体を 2 回コピーする必要があります。1 回は関数を呼び出すとき、もう 1 回は関数を返すときです。この方法は大きな配列の場合は非効率的であり、この関数を頻繁に呼び出すとパフォーマンスが低下する場合があります。
- ステータス変数などの他の目的に戻り値を使用できます。
return ステートメントを使用して呼び出し側に配列を返す方法が適切でない場合は、この配列を構造体内の要素として渡し、構造体内の配列値を変更します。呼び出し元ページでは、UDF に渡した構造体変数を使用して、変更されたデータにアクセスできます。
次のコードは、構造体内の配列を使用して前述の例を書き直した例です。この関数では、呼び出し元ページにステータス True を返し、構造体を使用して配列データを戻しています。
<cfscript> //Initialize some variables. //This creates a simple array as an element in a structure. arrayStruct=StructNew(); arrayStruct.Array=ArrayNew(1); arrayStruct.Array[1]=2; arrayStruct.Array[2]=22; //Define the function. function doubleOneDArrayS(OneDArrayStruct) { var i = 0; for ( i = 1; i LE arrayLen(OneDArrayStruct.Array); i = i + 1) { OneDArrayStruct.Array[i] = OneDArrayStruct.Array[i] * 2; } return True; } //Call the function. Status = doubleOneDArrayS(arrayStruct); WriteOutput("Status: " & Status); </cfscript> </br> <cfdump var="#arrayStruct#">
呼び出し元ページと関数では、配列を参照するために同じ構造体要素名(この場合は Array)を使用します。
Arguments スコープについて
関数のすべての引数は、固有のスコープである Arguments スコープに属しています。
Arguments スコープは、関数呼び出しが存続している間のみ存在します。関数が戻ると、このスコープとその変数は廃棄されます。
ただし、Arguments スコープが廃棄されても、関数に参照が渡された変数 (構造体やクエリーオブジェクトなど) は廃棄されません。呼び出し元ページで引数として使用した変数は存在し続け、関数によって引数に加えられた変更は、呼び出し元ページのその変数に反映されます。
Arguments は特殊なスコープで、配列または構造体として扱うことができます。この Arguments スコープの二面性を利用すると、次のような状況で引数を簡単に処理できます。
- CFScript を使用して関数を定義する。
- 関数を定義するには、 cffunction タグを使用します。
- 引数名 = 値という形式を使用して引数を渡す。
- 引数値のみを列挙して渡す。
- 関数に、宣言されていないオプションの引数を渡す。
Arguments スコープの内容
Arguments スコープとその内容には次のルールが適用されます。
- このスコープには、関数に渡されたすべての引数が含まれています。
- 関数の定義に cffunction を使用するた場合は、関数に引数が渡されなくても、宣言した引数ごとにエントリの「スロット」が常に用意されます。宣言した(オプションの)引数が渡されなかった場合、その引数に対応するスコープエントリは空になります。CFScript で定義した関数を呼び出す場合は、関数定義で宣言したすべての引数に値を渡します。したがって、CFScript 呼び出しの Arguments スコープに空のスロットはありません。
このルールを次の例に示します。次のように関数が宣言されているとします。
<cffunction name="TestFunction"> <cfargument name="Arg1"> <cfargument name="Arg2"> </cffunction>
この関数は、次の行のように、1 つの引数を使用して呼び出すことができます。
<cfset TestFunction(1)>
その結果、Arguments スコープは次のようになります。
|
|
構造体として使用 |
|
---|---|---|---|
エントリ |
値 |
エントリ |
値 |
1 |
1 |
Arg1 |
1 |
2 |
未定義 |
Arg2 |
未定義 |
この例では、スコープには引数が 2 つ定義されているので、次の関数は値 2 を返します。
ArrayLen(Arguments) StructCount(Arguments)
ただし、Arguments スコープの 2 番目の要素の内容は未定義なので、次のテストは false を返します。
Isdefined("Arguments.Arg2") testArg2 = Arguments[2]> Isdefined("testArg2")
IsDefined 関数では、配列要素が存在するかどうかはテストできません。配列の要素を検索するには、ArrayContains 関数を使用します。
配列としての Arguments スコープの使用
Arguments スコープを配列として参照する場合には、次のルールが適用されます。
- 名前を使用せずに引数を渡した場合、配列のインデックスは、関数呼び出しでの引数の位置を表します。
- 名前を使用して引数を渡した場合、配列のインデックスは、関数定義で引数が宣言されている位置を表します。
- 名前を使用して 引数を 渡し、関数定義で宣言されている引数の一部を渡さなかった場合、Arguments 配列では、渡さなかった引数に対応するインデックスが空のエントリになります。このルールは、 cffunction タグで使用)
- 名前を使用して、関数定義で宣言されていないオプション引数を渡した場合、その引数の配列インデックスは、次の値の合計になります。
- 関数定義で名前を使用して宣言されている引数の数
- その関数に渡した、関数定義で名前が宣言されていない引数の中での、そのオプション引数の順番
ただし、このように引数名を使用するのは、よいプログラミングスタイルとはいえません。関数を呼び出すときに常に同じオプション引数名を使用するとは限らないからです。
これらのルールを次の例に示します。この例では、関数の Arguments 配列の内容を表示する簡単な関数を定義し、様々な引数の組み合わせを使用して関数を呼び出します。
<cffunction name="TestFunction" > <cfargument name="Arg1"> <cfargument name="Arg2"> <cfloop index="i" from="1" to="#ArrayLen(Arguments)#"> <cfoutput>Argument #i#: #Arguments[i]#<br></cfoutput> </cfloop> </cffunction> <strong>One Unnamed argument</strong><br> <cfset TestFunction(1)> <strong>Two Unnamed arguments</strong><br> <cfset TestFunction(1, 2)> <strong>Three Unnamed arguments</strong><br> <cfset TestFunction(1, 2, 3)> <strong>Arg1:</strong><br> <cfset TestFunction(Arg1=8)> <strong>Arg2:</strong><br> <cfset TestFunction(Arg2=9)> <strong>Arg1=8, Arg2=9:</strong><br> <cfset TestFunction(Arg1=8, Arg2=9)> <strong>Arg2=6, Arg1=7</strong><br> <cfset TestFunction(Arg2=6, Arg1=7)> <strong>Arg1=8, Arg2=9, Arg3=10:</strong><br> <cfset TestFunction(Arg1=8, Arg2=9, Arg3=10)> <strong>Arg2=6, Arg3=99, Arg1=7</strong><br> <cfset TestFunction(Arg2=6, Arg3=99, Arg1=7)>
Arguments スコープは配列として使用できますが、IsArray(Arguments) 関数は常に false を返し、 cfdump タグはスコープを構造体として表示します。
構造体としての Arguments スコープの使用
Arguments スコープを構造体として参照する場合には、次のルールが適用されます。
- 引数名を構造体のキーとして使用します。例えば、関数定義に Principal 引数が含まれている場合は、Arguments.Principal のようにして引数を参照します。次のルールも適用されますが、これらのルールを使用したコードは作成しないでください。プログラムをわかりやすくするために、関数定義で名前を使用して宣言した引数のみに Arguments 構造体を使用してください。関数定義で宣言していないオプション引数には、Arguments スコープを配列として使用してください。
- 関数定義でオプション引数に名前を付けておらず、関数呼び出しでその名前を指定している場合は、関数呼び出しで指定した名前が使用できます。例えば、名前のないオプションの引数があり、その引数に myOptArg という名前を指定して関数を呼び出した場合は、関数の本文でその引数を Arguments.myOptArg として参照できます。ただし、これはよいプログラミングスタイルとはいえません。関数定義の内容が、その関数を呼び出すコードで使用されている名前に依存してしまうからです。
CFScript での Arguments スコープの使用
関数は、オプションの引数を取る場合があります。オプション引数は、関数を呼び出すときに必ずしも指定する必要はありません。関数に渡された引数の数を調べるには、次の関数を使用します。
ArrayLen(Arguments)
function SumN(Arg1,Arg2) { var arg_count = ArrayLen(Arguments); var sum = 0; var i = 0; for( i = 1 ; i LTE arg_count; i = i + 1 ) { sum = sum + Arguments[i]; } return sum; }
この関数では、次のどの関数呼び出しも有効です。
SumN(Value1, Value2) SumN(Value1, Value2, Value3) SumN(Value1, Value2, Value3, Value4)
など。
このコードでは、Arg1 や Arg2 という引数変数を直接使用していません。これは、これらの値が常に Arguments 配列の最初の 2 つの要素であり、配列を順にループしたほうが処理が簡単だからです。関数定義で Arg1 および Arg2 を使用すると、関数に引数が 1 つしか渡されなかった場合や 1 つも渡されなかった場合に、エラーが発生します。
関数本文で必須引数を参照するときは、引数名を使用する方法と、Arguments スコープの配列または構造体で位置を指定する方法を混在させないでください。2 つの方法を混在させると、混乱やエラーが発生しやすくなります。
cffunction 定義での Arguments スコープの使用
関数を定義するのに cffunction タグを使用する際に、 cfargument タグで処理するページのみに適用されます。Arguments スコープ識別子を使用する場合は、About the Arguments scopeに示したルールに従ってください。
CFScript で定義した関数内で Arguments スコープを使用する方法の詳細については、Using the Arguments scope in CFScriptを参照してください。
関数内変数
関数では、Arguments スコープだけでなく、 関数内にのみ存続する変数を使用することができます。 これらの変数は、同じ関数を次に呼び出しても、前回の値は保存されていません。関数が終了すると、このスコープ内の変数はすべて削除されます。
CFScript で関数内変数を作成するには、var ステートメントを使用します。他の変数と異なり、関数内変数にスコープ名の接頭辞を付ける必要はありません。
関数内変数の使用
CFScript の UDF で、関数固有の変数(ループインデックスや、関数呼び出しの間でのみ必要な一時変数など)を宣言するときは、常に var ステートメントを使用してください。これによって、これらの変数が関数内でのみ使用可能になり、他のスコープの変数名と競合することが避けられます。呼び出し元ページに同じ名前の変数があっても、この 2 つの変数は独立しており、互いに影響を与えることはありません。
例えば、ColdFusion ページに cfloop タグでインデックス変数 i を使用しており、タグ本文で呼び出している CFScript の UDF でも関数内変数としてループインデックス i を使用している場合、呼び出し元ページのループインデックスの値が UDF によって変更されることはありません。同様に、UDF のインデックスが呼び出し元ページによって変更されることもありません。したがって、この cfloop タグの本文でこの関数を呼び出しても問題はありません。
一般に、CFScript の UDF 内でのみ使用する変数は、関数の引数や共有スコープ変数を除き、すべて var ステートメントを使用して宣言します。ただし、例えば関数が呼び出されるたびにインクリメントされるカウンタなど、複数の関数呼び出しにまたがって変数値を保持する必要がある場合は、別のスコープを使用します。
呼び出し側の変数の参照
呼び出し元ページで使用可能な任意の変数を、そのページから呼び出された関数で使用したり変更したりすることができます。使用可能な変数としては、呼び出し側の Variables(ローカル)スコープの変数が含まれます。関数側では、その関数が呼び出し元ページに含まれているかのように、呼び出し側の変数にアクセスすることができます。例えば、呼び出し元ページに Customer_name というローカル変数があり、関数スコープに Customer_name という変数がない場合は、関数で Customer_name または Variables.Customer_name という参照(後者のほうが良いコーディングスタイルです)を使用すれば、その変数の読み取りや変更をおこなえます。同様に、関数内で作成したローカル変数を、関数呼び出しの後に、呼び出し元ページの任意の場所で使用することができます。関数を呼び出す前に変数を使用することはできません。
ただし、通常は、呼び出し側の変数を関数内で直接使用するのは避けてください。呼び出し側の変数を使用すると、呼び出し側との間に依存関係ができることになります。呼び出し側のコードでは、関数で使用されている変数名と同一の変数名を常に使用する必要があります。多くのページからこの関数を呼び出す場合、これを守るのは容易ではありません。
呼び出し側と関数の間でデータのやり取りをおこなうときは、これらの問題を避けるため、関数の引数と戻り値のみを使用してください。呼び出し元ページの変数を関数内で直接参照しないでください。これによって、呼び出し側コードの変数を気にすることなく、アプリケーション内(または複数のアプリケーション)の任意の場所からその関数が使用できるようになります。
ただし、実際のプログラミングでは、この推奨事項にも例外があります。例えば、次のことが可能です。
- Application スコープや Session スコープのカウンタ変数などの、共有スコープ変数を使用できます。
- Request スコープを使用して、関数内で使用する変数を保存できます。詳細については、「スタティック変数および定数での Request スコープの使用」を参照してください。
- 呼び出し側と関数側で常に同じ変数名を使用するように徹底できるのであれば、呼び出し側のデータを直接操作する文脈依存型の関数を作成できます。
呼び出し側の単純変数(関数に参照が渡されない変数)を関数で直接変更する必要がある場合は、構造体の中にその変数を格納して、その構造体を引数として関数に渡します。
引数の使用
関数の引数名は、呼び出し側の変数名と同じにすることができますが(値は同じにはなりません)、コードがわかりにくくなるので、このようなことはおこなわないでください。
引数の存続期間については次のルールが適用されます。
- ColdFusion では、 単純 変数および配列の引数はその値が渡されるので、その名前と値は関数が実行されている間のみ存在します。
- 構造体、クエリ、COM オブジェクトなどのオブジェクトはその参照が渡されるので、引数の名前は関数が実行されている間のみ存在しますが、データは関数が戻った後でも存続し、呼び出し側の変数名を使用してアクセスできます。呼び出し側の変数と引数には異なる名前を付けることができます。
関数内変数と同じ名前を持つ別のスコープの変数を関数で使用する必要がある場合は、その変数に Variables や Form などのスコープ識別子の接頭辞を付けてください。ただし、他のスコープの変数を関数内で直接使用するのは、多くの場合よいコーディングスタイルではありません。