クロージャが定義される状況
クロージャは内部関数です。内部関数は、外部関数の変数にアクセスできます。外部関数にアクセスすることで、内部関数にアクセスできます。次の例を参照してください。
<cfscript>
function helloTranslator(required String helloWord) {
return function(required String name) { return "#helloWord#, #name#"; }
;
}
helloInFrench=helloTranslator("Bonjour")
writeOutput(helloInFrench("John"))
</cfscript>
この例では、外部関数はクロージャを返します。外部関数には helloHindi 変数を使用してアクセスします。この関数は helloWord 引数を設定します。この関数ポインターを使用して、クロージャを呼び出します。例えば、helloInHindi("Anna") のようにします。外部関数の実行後であっても、クロージャは外部関数によって変数セットにアクセスできることに注意してください。
この場合、クロージャを使用して 2 つの新しい関数が作成されます。1 つは名前に Namaste を追加します。もう 1 つは名前に Bonjour を追加します。helloInHindi および helloInFrench はクロージャです。これらの関数の本文は同じですが、異なる環境を格納します。
内部関数は、外部関数が返った後でも実行できます。内部関数が実行可能な場合に、クロージャが形成されます。
例で示されているように、外部関数から返った後でも、内部関数は外部関数の変数にアクセスできます。クロージャは、作成時の環境への参照を保持しています。例えば、外部関数のローカル変数の値などです。これにより、クロージャは簡単に使用できる便利な機能になります。
クロージャについて詳しくは、http://jibbering.com/faq/notes/closures を参照してください。
ColdFusion でのクロージャ
クロージャは次のようなカテゴリにできます。
- 名前を指定しないでインラインで定義。次のようにして使用できます。
変数、配列項目、構造体および変数スコープに代入できます。関数から直接返すことができます。
例
<cfscript>
function operation(required string operator){
return function(required numeric x, required numeric y){
if(operator == "add")
{
return x + y;
}
else if(operator == "subtract"){
return x - y;
}
}
}
myval_addition=operation("add");
myval_substraction=operation("subtract");
writeoutput(myval_addition(10,20));
writeoutput(myval_substraction(10,20));
</cfscript>
この例では、外部関数は演算子を設定します。myval_addition および myval_substraction は 2 つのクロージャです。これらは、外部関数によって設定された条件に基づいてデータを処理します。
関数およびタグ引数としてインラインで定義。例
<cfscript>
function operation(required numeric x, required numeric y, required function logic)
{
result=logic(x,y);
return result;
}
add = operation(10,20, function(required numeric N1, required numeric N2)
{
return N1+N2;
});
subtract = operation(10,20, function(required numeric N1, required numeric N2)
{
return N1-N2;
});
</cfscript>
<cfdump var="#add#">
<cfdump var="#subtract#">
この例では、関数 operation の引数 logic がクロージャです。operation を呼び出すとき、インラインクロージャが引数として渡されます。この匿名クロージャには、数値を処理するためのロジックが含まれます(addition または subtraction)。この場合、ロジックは動的であり、クロージャとして関数に渡されます。
クロージャは変数に代入可能
変数にクロージャを代入できます。
例
var c2 = function () {..}
変数にクロージャを代入する場合は、スクリプト形式のシンタックスのみがサポートされます。
クロージャは戻り値の型として使用可能
クロージャを戻り値の型として使用できます。
戻り値の型がクロージャの場合は、Function キーワードの先頭を大文字にすることをお勧めします。
例
Function function exampleClosure(arg1)
{
function exampleReturned(innerArg)
{
return innerArg + arg1;
}
/*
return a reference to the inner function defined.
*/
return exampleReturned;
}
キーと値のペアによるクロージャの呼び出し
関数呼び出しの場合と同じように、キーと値のペアを渡してクロージャを呼び出すことができます。
例
var c2 = function(arg1, arg1) {..}
c2(arg1=1, arg2=3);
関数の外でクロージャを変数に代入可能
関数の外で変数にクロージャを代入できます。
例
hello = function (arg1)
{
writeoutput("Hello " & arg1);
};
hello("Mark");
引数のコレクションによるクロージャの呼び出し
var c2 = function(arg1, arg1) {..}
argsColl = structNew();
argsColl.arg1= 1;
argsColl.arg2= 3;
c2(argumentCollection = argsColl);
クロージャと関数
クロージャは、作成時に認識できるように変数のコピーを保持します。グローバル変数(ColdFusion 固有スコープなど)およびローカル変数(宣言または外部関数のローカルおよび引数スコープを含む)は、クロージャの作成時に保持されます。関数は静的です。
次の表では、定義される方法に基づくクロージャのスコープの詳細を示します。
|
|
スコープ |
|---|---|
|
CFC 関数内 |
クロージャ引数スコープ、外側の関数のローカルスコープおよび引数スコープ、this スコープ、変数スコープ、スーパースコープ |
|
CFM 関数内 |
クロージャ引数スコープ、外側の関数のローカルスコープおよび引数スコープ、this スコープ、変数スコープ、スーパースコープ |
|
関数引数として |
クロージャ引数スコープ、変数スコープ、this スコープ、スーパースコープ(CFC コンポーネント内で定義されている場合) |
クロージャでは、スコープが指定されていない変数の検索順序は次のとおりです。
-
クロージャのローカルスコープ
-
クロージャの引数スコープ
-
可能な場合は、外部関数のローカルスコープ
-
可能な場合は、所有者関数のローカルスコープ
-
ColdFusion のビルトインスコープ
クロージャはユーザー定義関数を呼び出せません。これは、クロージャのコンテキストは保持されますが、関数のコンテキストは保持されないためです。これにより、誤った結果になります。例えば、クロージャがキャッシュされた場合、クロージャは後で適切に呼び出して使用できますが、関数は使用できません。
クロージャ関数
クロージャ関数は次のとおりです。
isClosure
説明
クロージャの名前かどうかを調べます。
戻り値
名前をクロージャとして呼び出せる場合は True、呼び出せない場合は False。
カテゴリ
決定関数
isClosure(closureName)
関連項目
他の決定関数。
履歴
ColdFusion 10:この関数が追加されました。
パラメーター
|
パラメーター |
説明 |
|---|---|
|
closureName |
クロージャの名前です。引用符で囲まないでください。定義されている変数名または関数名でない場合は、エラーになります。 |
使用方法
クロージャの名前かどうかを判定するには、この関数を使用します。
例
<cfscript>
isClosure(closureName)
{
// do something
}
else
{
// do something
}
</cfscript>
関数 isCustomFunction に対する変更
クロージャは関数オブジェクトですが、カスタム関数とは見なされません。
関数は次の値を返すようになっています。
- True:名前をカスタム関数として呼び出せる場合。
- False:名前をクロージャとして呼び出せる場合。
使用例
次の例では、ColdFusion のクロージャを効果的に使用できる方法を説明します。
例 - クロージャを使用した配列のフィルター処理
次の例では、所在地、年齢、呼称に基づいて従業員をフィルター処理します。フィルター処理には単一の関数を使用します。フィルター処理のロジックは、クロージャとして関数に提供されます。そのフィルター処理ロジックは動的に変更されます。
例
- 変数を定義する employee.cfcfile を作成します。
/**
* @name employee
* @displayname ColdFusion Closure Example
* @output false
* @accessors true
*/
component
{
property string Name;
property numeric Age;
property string designation;
property string location;
property string status;
}
- 従業員の配列を作成します。この CFC には filterArray() }} 関数も含まれます。クロージャ {{filter は、関数の引数です。この関数にアクセスするときに、フィルター処理のロジックをクロージャとして渡します。
<!---filter.cfc--->
<cfcomponent>
<cfscript>
//Filter the array based on the logic provided by the closure.
function filterArray(Array a, function filter)
{
resultarray = arraynew(1);
for(i=1;i<=ArrayLen(a);i++)
{
if(filter(a[i]))
ArrayAppend(resultarray,a[i]);
}
return resultarray;
}
function getEmployee()
{
//Create the employee array.
empArray = Arraynew(1);
ArrayAppend(empArray,new employee(Name="Ryan", Age=24, designation="Manager", location="US"));
ArrayAppend(empArray,new employee(Name="Ben", Age=34, designation="Sr Manager", location="US"));
ArrayAppend(empArray,new employee(Name="Den", Age=24, designation="Software Engineer", location="US"));
ArrayAppend(empArray,new employee(Name="Ran", Age=28, designation="Manager", location="IND"));
ArrayAppend(empArray,new employee(Name="Ramesh", Age=31, designation="Software Engineer", location="IND"));
return empArray;
}
</cfscript>
</cfcomponent>
- フィルター処理のロジックを提供するクロージャを指定して {{filterArray()}} 関数にアクセスする CFM ページを作成します。{{filterArray()}} 関数は、従業員データを所在地、年齢、呼称の 3 つの方法でフィルター処理するために使用されます。関数にアクセスするたびに、クロージャ内のフィルター処理ロジックが変更されます。
<!---arrayFilter.cfm--->
<cfset filteredArray = arraynew(1)>
<cfset componentArray = [3,6,8,2,4,7,9]>
<cfscript>
obj = CreateObject("component", "filter");
// Filters employees from India
filteredArray = obj.filterArray(obj.getEmployee(), function(a)
{
if(a.getLocation()=="IND")
return 1;
else
return 0;
});
writedump(filteredArray);
//Filters employees from india whos age is above thirty
filteredArray = obj.filterArray(obj.getEmployee(), closure(a)
{
if((a.getLocation()=="IND") && (a.getAge()>30))
return 1;
else
return 0;
});
writedump(filteredArray);
// Filters employees who are managers
filteredArray = obj.filterArray( obj.getEmployee(), function(a)
{
if((a.getdesignation() contains "Manager"))
return 1;
else
return 0;
});
writedump(filteredArray);
</cfscript>
ColdFusion(2018 リリース)アップデート 5 のラムダ
ラムダとは
ColdFusion では、ラムダを使用してユーザー定義関数を簡単に記述できます。ラムダはアロー演算子(=>)に基づいています。この演算子は「ファットアロー」とも呼ばれます。
例えば、従来、ColdFusion では UDF を次のように記述してきました。
<cfscript>
// Create function mul that takes arguments a and b
function mul(a,b){
return a*b
}
// store the value of function in the variable c
c=mul(3,4)
WriteOutput("The product is: " & c)
</cfscript>
アロー演算子を使用すると、上記のコードを次のように書き換えることができます。
<cfscript>
// Create function mul
mul=(a,b)=>{return a*b} // Use the arrow operator
// store the value of function in the variable c
c=mul(3,4)
WriteOutput("The product is: " & c)
</cfscript>
両方の例を比較すると、2 番目の例の方がはるかに簡潔で解釈しやすいことがわかります。2 番目の例では、より少ないコード行で同じ結果を実現しています。
シンタックス
(param1, param2, …, paramN) => { statements }
次に例を示します。
<cfscript>
sumLambdaFunc = (a,b) => {
sum=a + b;
return sum;
}
writeOutput("The sum of a and b is: " & sumLambdaFunc(7,5))
</cfscript>
(param1, param2, …, paramN) => return statement
次に例を示します。
<cfscript>
multiplyLambdaFunc = (x,y) => x*y
writeOutput("The product of x and y is: " & multiplyLambdaFunc(7,5))
</cfscript>
param1 => return statement
次に例を示します。
<cfscript>
squareLamdaFunc = x => x*x
writeOutput("The square of x is: " & squareLamdaFunc(7))
</cfscript>
関数本体に return ステートメントが 1 つしかない場合は、ラムダ関数の本体に中括弧を使用する必要はありません。
次に例を示します。
x => x > 10 // これは次のように解釈されます:(x) => {return x > 10 ;}
パラメーターのない関数の場合は、次のように、一対の小括弧で関数を記述します。
<cfscript> message=()=>return "Hello World!" WriteOutput(message()) </cfscript>
暗黙の return を使用すると、上記は次のように書き換えることができます。
<cfscript> message=()=> "Hello World!" WriteOutput(message()) </cfscript>
関数引数としての配列オブジェクト
例えば、配列では、配列オブジェクトをアロー関数の引数として返すことができます。例えば、次のコードは、空のクロージャ関数がアロー演算子で渡されると、配列値を返します。
<cfscript>
myarray = [() => {return 1}, () => {return 2}]
writedump(myarray)
writeOutput(myarray[1]()) // value 1
writeOutput(myarray[2]()) // value 2
</cfscript>
出力
1
2
同様に、関数の引数を配列値として使用することができます。次に例を示します。
<cfscript>
newArray =[(x,y) => {return x+y;}, (x,y) => {return x*y}, (x,y)=> {return x-y} ];
Writedump(newArray)
Writedump(newArray[1](6,4))
Writedump(newArray[2](6,4))
Writedump(newArray[3](6,4))
</cfscript>
出力
さらに、次のコードでは、配列値を関数の引数として使用する方法がわかります。
<cfscript>
array2 = [(arg1) => { return arg1+1 }, (arg1) => { return arg1 }]
writedump(array2)
writeDump(array2[1](3))
writeDump(array2[2]())
array3 = [(String arg1=NULL) => { return arg1 }, (String arg1=2) => { return arg1 }]
writedump(array3)
writeDump(array3[2]())
</cfscript>
オブジェクトリテラル
アロー演算子を使用して、オブジェクトリテラルを返すことができます。次に例を示します。
<cfscript>
// object literals
myfunction=()=>{return value= 'test'}
writedump(myfunction())
</cfscript>
引数としてのクロージャ
See the example below.
<cfscript>
function firstFunction1(function closure) {
if(isClosure(closure))
return closure();
return;
}
function secondFunction1(function closure,string arg1) {
return closure(arg1);
}
function thirdFunction1(string arg1, function closure, string arg2) {
return closure(arg1,arg2);
}
function fourthFunction1(function closure) {
return closure(insideClosure=() => {return 1;});
}
function fifthFunction1(function closure1, function closure2) {
return closure1(closure2);
}
</cfscript>
<cfscript>
output = firstFunction1(closure=() => {return "Hello";});
writeOutput(output);
</cfscript>
<cfscript>
output = secondFunction1(closure=(arg1) => {return arg1;},arg1="Hello");
writeOutput(output);
</cfscript>
<cfscript>
output = thirdFunction1(arg1="Hello",closure= (arg1,arg2) => { return arg1 & arg2;},arg2="World");
writeOutput(output);
</cfscript>
<cfscript>
output = fourthFunction1(closure = (insideClosure) => { return insideClosure();});
writeOutput(output);
</cfscript>
<cfscript>
output = fifthFunction1(closure1 = (closure2) => { return closure2();}, closure2 = () => { return 1;});
writeOutput(output);
</cfscript>
例 - ビルトイン関数でのアロー演算子
次の例では、関数型プログラミングの最も重要な 3 本の柱である map、filter、reduce のコードをラムダで簡潔、明快に記述する方法を示しています。
これらの例では、配列を使用しています。
map
map 関数は、配列のすべての要素を操作し、変換された要素値を持つ同じ次元の配列を生成します。
アロー演算子を使用すると、配列における map 関数を次のように実装できます。
<cfscript>
// array map using lambda
numbers=[1,4,6,9]
double=numbers.map((numbers)=>{return numbers*2})
writedump(double)
</cfscript>
出力
filter
map と同様に、filter は配列のすべての要素を操作し、1 つ以上の条件に応じて、フィルター処理したサブセット配列を返します。
アロー演算子を使用すると、配列における filter 関数を次のように実装できます。
<cfscript>
// array filter using lambda
superheroes=[
{"name":"Iron Man","member":"Avengers"},
{"name":"Wonder Woman","member":"Justice League"},
{"name":"Hulk","member":"Avengers"},
{"name":"Thor","member":"Avengers"},
{"name":"Aquaman","member":"Justice League"}
];
writedump(superheroes)
filtered=superheroes.filter((superheroes)=>{
return (superheroes.member=="Avengers")
})
writedump(filtered)
</cfscript>
reduce
reduce 関数は配列を単一の値に「縮約」します。アロー演算子を使用すると、reduce 関数を次のように実装できます。
<cfscript>
// array reduce using lambda
numbers=[1,3,5,7,9]
sum=numbers.reduce((previous,next)=>{
return previous+next
},0)
writeOutput(sum)
</cfscript>
メンバー関数の連結
ColdFusion(2018 リリース)では、メンバー関数のチェーン化が導入されました。アロー演算子を使用すると、関数をチェーン化し、コードフットプリントを大幅に削減することができます。
次に例を示します。
<cfscript>
// chaining of lambda functions
numbers=[1, 2, 4, 5, 6, 7, 7, 9, 11, 14, 43, 56, 89]
// find if number is even
isEven=(x)=>{return x%2==0}
// add 2 to number
addTwo=(x)=>{return x+2}
// chain the functions
result=numbers.filter(isEven).map(addTwo)
writedump(result)
</cfscript>
出力
ColdFusion タグにおけるアロー演算子
ColdFusion(2018 リリース)では、タグ内のクロージャが導入されました。次に例を示します。
<cfset myarray=[
{name="Thomas", age="22"},
{name="Zaza", age="36"},
{name="Novak", age="136"},
{name="Marin", age="361"},
{name="Rafa", age="3"},
{name="$bl0091@", age="-23"}
]>
<!--- define closure function --->
<cfset closure=function (e1,e2){
return compare(e1.name,e2.name);
}>
<cfset ar = arraySort(myarray,closure)>
<cfdump var="#myarray#">
ColdFusion(2018 リリース)アップデートでは、アロー演算子がタグでサポートされるようになりました。次に例を示します。
<cfset print=()=>{
return "Hello World"
}>
<cfoutput>
#print()#
</cfoutput>
上記のコードでは、「Hello World」という文字列を返す空の関数 print が作成されています。
ColdFusion タグでは、ネストされたアロー関数を組み込んで適切な出力を返すこともできます。次に例を示します。
<cfset myClosure= (default)=> {
return (default) =>
{
return ()=> {
return "Hello World"
}
}}>
<cfoutput>#myClosure("testing arrow in nested levels")()()#</cfoutput>
Also,
<cfset myClosure= (default)=> {
return (default) =>
{
return (string s)=> {
return s
}
}}>
<cfoutput>#myClosure("testing arrow in nested levels")()("Hello World")#</cfoutput>
名前付き引数でのラムダの使用
関数内でアロー演算子を名前付き引数として使用することができます。次の例を参照してください。
例 1
<cfscript>
myArray1 = ["CF10","Zeus","CF9","Centaur"];
resultArray = ArrayFilter(array=myArray1,callback=(any arrayEntry) =>{
if(arrayEntry == "CF9" || arrayEntry == "Centaur")
return false;
return true;
});
for(var1 in resultArray) {
writeOutput(var1 & "<br>");
}
</cfscript>
例 2
<cfscript>
numArray = [10,20,30,150,400,99,100];
resultArray = ArrayFilter(array=numArray,callback=(any arrayEntry) =>{
if(arrayEntry < 100)
return false;
return true;
});
for(var1 in resultArray) {
writeOutput(var1 & "<br>");
}
</cfscript>
例 3
<cfscript>
myArray1 = ["ColdFusion","Hello","San Jose","Adobe","Systems"];
numArray = [60,3,30,4,500,44];
</cfscript>
<cfscript>
index = ArrayFind(array=myArray1,callback=(any object1) => {
if(object1 == "Systems")
return true;
return false;
});
writeOutput("Found at " & index);
</cfscript>
<cfscript>
index = ArrayFind(myArray1,(any object1) =>{
if(object1 == "Hello")
return true;
return false;
});
writeOutput("Found at " & index);
</cfscript>
タグのクロージャ
<cfscript>
// Define an array of structs
myArray = [
{name="Thomas", age="22"},
{name="Zaza", age="36"},
{name="Novak", age="136"},
{name="Marin", age="361"},
{name="Rafa", age="03"},
{name="$bl0091@", age="-23"}
];
// Define a closure function that sorts the names in the array of structs
callback=function (e1, e2){
return compare(e1.name, e2.name);
}
// Use the closure function
arraySort(myArray,callback);
// Display the sorted array of structs
WriteDump(myArray);
</cfscript>
一方、以下のスニペットは常に例外を返します。
<cfset myClosure= function() {…}>
ColdFusion(2018 リリース)では、タグでクロージャ関数を使用できます。次に例を示します。
<cfset myarray=[
{name="Thomas", age="22"},
{name="Zaza", age="36"},
{name="Novak", age="136"},
{name="Marin", age="361"},
{name="Rafa", age="3"},
{name="$bl0091@", age="-23"}
]>
<!--- define closure function --->
<cfset closure=function (e1,e2){
return compare(e1.name,e2.name);
}>
<cfset ar = arraySort(myarray,closure)>
<cfdump var="#myarray#">