Functions: Structure, Evaluation and Calling Scheme
There are only two user-created objects in the FISH language that can be executed: functions and operators.
The name of a function follows the fish define
command, can be followed by an optional list of arguments, and its scope terminates with the end statement.
The end statement also serves to return control to the caller when the function is executed.
(Note that the exit statement returns control prior to an end
statement.)
Consider this example that shows function construction and use.
fish define xxx
aa = 2 * 3
bb = 0
xxx = aa + bb
end
The value of xxx is changed when the function is executed. The variable aa is computed locally, but the existing value of bb is used in the computation of xxx. If values are not explicitly given to variables, they default to zero (integer). It is not necessary for a function to assign a value to the variable corresponding to its name.
An alternative method of returning a value is to use the return statement, as shown in the following example:
fish define yyy
aa = 2 * 3
return aa + bb
end
In this case, the value aa+bb is returned whenever yyy is called, but the actual FISH parameter associated with yyy is not changed.
A function may be invoked in a variety of ways, which include:
as the single word xxx inside a FISH function;
as the variable xxx in a FISH formula — e.g.,:
new_var = (math.sqrt(xxx) / 5.6)^4;
as a single word [xxx] enclosed in brackets as outlined here;
as a symbolic replacement for a number in a FLAC3D input line; or
as a parameter to a command that accept FISH functions as options (for example,
fish history
.). In this case the function will generally be invoked as a callback at a later time.as a special case of (5), a function may be executing during cycling by using the
fish callback
command or the fish-call keyword to themodel solve
command. Although in this case we recommend using operators for runtime efficiency.
FISH functions may also take an arbitrary number of arguments. When the function is defined these are indicated by including the arguments in the function definition. For instance:
fish define george(one,two)
george = one * two
end
A function may be referenced in another function before it is defined; the FISH compiler
simply creates a symbol at the time of first mention, and then links all references to the
function when it is defined by a fish define
command. A function cannot be deleted, but
it can be redefined.
Function calls may be nested to any level (i.e., functions may refer to other functions, which may refer to others, ad infinitum). Function calls may be recursive (i.e., execution of a function can invoke that same function), however a function call nesting limit of 64 is enforced. The next example shows an unbounded recursive function call, which is not allowed, because the name of the defining function is used in such a way that the function will try to call itself without limit. The example will produce an error on execution:
;; Bad recursion
data scalar create (0,0,0)
data scalar create (0,0,1)
data scalar create (1,0,0)
fish define pos_sum
pos_sum = (0,0,0)
loop foreach local scalar data.scalar.list
pos_sum = pos_sum + data.scalar.pos(scalar)
end_loop
end
![pos_sum]
In general, the rule is that a function should avoid retrieving its own value. The same function should be coded as shown:
;; No Recursion
fish define pos_sum
pos_sum = (0,0,0)
loop foreach local scalar data.scalar.list
pos_sum += data.scalar.pos(scalar)
end_loop
end
[pos_sum]
The difference between variables and functions is that a function is always executed whenever its name is mentioned, while a variable simply conveys its current value. However, the execution of a function may cause other variables (as opposed to functions) to be evaluated. This effect is useful, for example, when several histories of FISH variables are required: only one function is necessary in order to evaluate several quantities, as below:
model new
fish define h_var_1
global bp22 ; pointer to grid point with ID = 22, set during creation...
global bp45 ; pointer to grid point with ID = 45, set during creation...
local xx = gp.pos.x(bp45)
h_var_1 = xx
h_var_2 = gp.pos.x(bp22)
h_var_3 = math.abs(h_var_2 - xx) ; use of xx here avoids recursion
h_var_4 = gp.vel.x(bp45)
h_var_5 = gp.vel.x(bp22)
h_var_6 = math.abs(h_var_5 - h_var_4)
end
The FLAC3D commands to request histories might be:
fish history h_var_1
fish history h_var_2
fish history h_var_3
fish history h_var_4
fish history h_var_5
fish history h_var_6
The function h_var_1 would be executed by FLAC3D’s history logic at a specified interval. But as a side effect, the values of h_var_2 through h_var_6 would also be updated.
Was this helpful? ... | Itasca Software © 2024, Itasca | Updated: Dec 19, 2024 |