JSLでは、ローカル変数を引数とした関数を作成することもできます。関数は、Function関数によって作成されます。関数の作成は、マクロの概念を拡張したものと言えます。例として、平方根を求める関数を作成します。その関数では引数が負の場合はエラーではなくゼロを戻すようにします。まず、中括弧{ }を使ってローカル引数のリストを指定してから、式を直接入力します。Functionで指定した式はその場では評価されず、式として保存されるため、Exprで式を囲む必要はありません。
myRoot = Function( {x},
If( x > 0, Sqrt( x ), 0 )
);
a = myRoot( 4 ); // 2を戻す
b = myRoot( -1 ); // 0を戻す
関数は値と同じように、変数に保存されます。このため、Rootという関数とRootという変数の両方を持つことはできません。また、関数それ自体の内部にいるときを除き、いつでも関数を再定義できることも意味しています。
関数は呼び出されると、引数を評価し、第1引数のリストで指定されているローカル変数にその引数を与えます。次に関数の本体、つまり第2引数を評価します。
引数の値は、関数の一時使用のためのものです。関数が終了すると、値は捨てられます。戻される唯一の値は、戻り値です。複数の値を戻したい場合は、1つの値ではなく、リストにして戻すようにしてください。
関数を定義した場合、関数内部の式そのものにアクセスすることはできません。これはName Exprコマンドを使用しても同様です。関数内部の式にアクセスするには、その関数全体をexpr()節の中に入れる必要があります。たとえば、
makeFunction = Expr(
myRoot = Function( {x},
If( x > 0, Sqrt( x ), 0 )
)
);
d = Substitute( Name Expr( MakeFunction ), Expr( x ), Expr( y ) );
Show( d );
makeFunction;
オプションの引数を作成することもできます。
例:
f1 = Function( {x, y, z}); // すべての引数が必須
f2 = Function( {x, y=2, z=4}); // xが必須、yとzはオプション
メモ:
• 引数をオプションにするには、関数を定義するときにその引数にデフォルト値を指定します(x ではなくx=1)。
• オプションの引数は必須の引数の後に指定する必要があります。たとえば、次の例は使用できません。
Function( {x = 1, y}, ... )
この関数を定義するときにはエラーは発生しませんが、指定したデフォルト値は使用されないため、呼び出す際xとyに値を渡さなければエラーとなります。
• この関数を呼び出すとき、たとえオプションの引数であっても、途中のものを飛ばしてその後ろのものを指定することはできません。次の例でzの値を指定するには、必ずyの値も指定する必要があります。
ex = Function( {x, y = 2, z = 3},
Return( x + y + z )
);
ex( 1, 4 ); // xに1、yに4が渡され、zは3になるため、8を戻す
変数が関数のローカルであることを宣言し、グローバルシンボルスペースに影響されないようにすることができます。これは特に再帰関数で便利です。再帰関数では、関数呼び出しの各レベルでローカル変数の値を分けておく必要があるためです。
前述したように、関数を定義するには次のように指定します。
functionName=Function({arg1, ...}, body);
次のように指定すると、適用範囲が明示されていない名前すべてをローカル変数として扱うようになります。
functionName=Function({arg1, ...}, {Default Local}, body);
作成時にDefault Localを使用すると、次の修飾条件を満たす名前がすべてローカルになります。
• グローバルとして適用範囲が指定(たとえば::name)されていない名前
• データテーブルとして適用範囲が指定(たとえば:name)されていない名前
• それらの後に括弧なしで続く名前(たとえばname(...)という形式でないもの)
次の例では、3つの数字を合計する関数を定義しています。
add3 = Function( {a, b, c},
{temp},
temp = a + b;
temp + c;
);
X = add3( 1, 5, 9 );
15
次の関数も同じ処理をしますが、ローカル変数を自動的に設定します。
add3 = Function( {a, b, c},
{Default Local},
temp = a + b;
temp + c;
);
X = add3( 1, 5, 9 );
15
どちらの場合も、変数tempはグローバル変数ではなく、たとえ関数の外で同じ名前の変数がグローバル変数として設定されていても、そのグローバル変数は関数の実行に影響されません。
ユーザ定義の関数内でDefault Localを使用した場合、コンテキストによって動作が異なるため、混乱を招く場合があります。つまり、同名の変数がスコープの中と外に存在する場合、同じ関数でも動作が異なる可能性があります。これは、Default Localを使用しても非修飾名に対する参照規則は変わらないためです。関数がコンテキストに依存しないようにするには、ローカルにしたい変数をすべて列挙する必要があります。そうすれば、スコープの外にある変数の値についての混乱や誤りを最小限に抑えることができます。