JSL also has a function called Function to extend the macro concept with a local context arguments. Suppose that you want to create a function that takes the square root but tolerates negative arguments, returning zero rather than errors. You first specify the local arguments in a list with braces { } and then state the expression directly. You do not need to enclose the expression in Expr because Function stores it as an expression implicitly.
myRoot = Function( {x},
If( x > 0, Sqrt( x ), 0 )
);
a = myRoot( 4 ); // returns 2
b = myRoot( -1 ); // returns 0
Functions are stored in variables, the same as values. This means that you cannot have both a root function and a root value. It also means that you can redefine a function anytime except when you are inside the function itself.
When a function is called, its arguments are evaluated and given to the local variables specified in the list forming the first argument. Then the body of the function, the second argument, is evaluated.
The values of the arguments are for the temporary use of the function. When the function is exited, the values are discarded. The only value returned is the return value. If you want to return several values, then return a list instead of a single value.
In defined functions, the stored function is not accessible directly, even by the Name Expr command. If you need to access the function expression in your script, you have to create the function within an expr() clause. For example,
makeFunction = Expr(
myRoot = Function( {x},
If( x > 0, Sqrt( x ), 0 )
)
);
d = Substitute( Name Expr( MakeFunction ), Expr( x ), Expr( y ) );
Show( d );
makeFunction;
You can create optional arguments. For example:
f1 = Function( {x, y, z}); // all arguments are required
f2 = Function( {x, y=2, z=4}); // x is required, y and z are optional
Notes:
• To make an argument optional, give it a default value in the function's definition (x=1 rather than just x).
• Optional arguments always must follow a required argument. For example, this is not allowed:
Function( {x = 1, y}, ... )
It will work when defined, but you will always have to pass in a value for x and y, because the default value will never be used.
• When you call the function, you cannot skip optional arguments. In this example, you have to supply a value for y to supply a value for z when you call the function:
ex = Function( {x, y = 2, z = 3},
Return( x + y + z )
);
ex( 1, 4 ); // passes in 1 for x and 4 for y; z will be 3. returns 8.
You can declare variables as local to a function so that they do not affect the global symbol space. This is particularly useful for recursive functions, which need to keep separate the values of the local variables at each level of function call evaluation.
As shown above, a function definition looks like this:
functionName=Function({arg1, ...}, body);
You can also have the function definition default all the unscoped names to be local.
functionName=Function({arg1, ...}, {Default Local}, body);
For the purposes of creation, the use of Default Local localizes all the names that meet the following qualifications:
• Are not scoped as globals (for example, ::name)
• Are not scoped as data table column names (for example, :name)
• Occur without parentheses after them (for example, are not of the form name(...))
For example, the following function sums three numbers.
add3 = Function( {a, b, c},
{temp},
temp = a + b;
temp + c;
);
X = add3( 1, 5, 9 );
15
The following function does the same thing, automatically finding locals.
add3 = Function( {a, b, c},
{Default Local},
temp = a + b;
temp + c;
);
X = add3( 1, 5, 9 );
15
In both cases, the variable temp is not a global, or, if it is already a global, remains untouched by evaluating the functions.
Using Default Local in user-defined functions can cause some confusion because it is context-sensitive. That is, the same function may behave differently in different contexts (depending on whether same-named outer variables are in scope) because the reference rules for unqualified names are unchanged when using Default Local. To make a function less context-sensitive, enumerate each and every variable that you want to be local. This reduces the confusion and the potential incorrect use of outer scope variable values.