The following sections include various examples of procedure definition directly from the command-line. Of course in practice one would normally create such code in files, which would be sourced.
Tcl has facilities for automatically defining undefined commands
when an attempt is made to execute them. In particular, the array
auto_index contains the commands to define indexed
commands.
Users with small libraries of their own procedures may prefer to
simply source the relevant files as part of Tcl startup. The startup
files distributed with NAP automatically source any file called
my.tcl in the home directory. This file can contain
source commands to define one's own procedures.
[] facility.
sind
sind". The procedure can be defined on one line as
follows:
% proc sind degrees {nap "sin(1r180p * degrees)"}
Note that "1r180p" is the constant π/180. Now let's
test function "sind":
% nap "x = 0 .. 180 ... 30"
::NAP::76-76
% nap "y = sind x"
::NAP::83-83
% [nap "transpose(x /// y)"]
0 0
30 0.5
60 0.8660254
90 1
120 0.8660254
150 0.5
180 1.224606e-16
lam
Now let's define a function (with two arguments x and y)
defined by the above expression
"transpose(x /// y)".
This is the transpose of the
laminated arguments, so let's call it "lam".
% proc lam {
x
y
} {
nap "z = x /// y"
nap "transpose z"
}
There are two lines in the body of this procedure. The result of
the final line defines the result of the function. Testing:
% [nap "lam(x,y)"]
0 0
30 0.5
60 0.8660254
90 1
120 0.8660254
150 0.5
180 1.224606e-16
get_bin
Now let's define a function "get_bin" for binary input using the "nap_get" command:
% proc get_bin {
filename
{datatype {'f32'}}
{swap 0}
} {
# convert all arguments to strings
set filename [[nap "filename"]]
set datatype [[nap "datatype"]]
set swap [[nap "swap"]]
set channel [open $filename]
nap "in = [nap_get [lindex {binary swap} $swap] $channel $datatype]"
close $channel
nap "in"; # Define result
}
Note that the arguments "datatype" and "swap" have default values. Also note how all three
arguments are converted from NAP expressions to Tcl strings.
Now let's test it. The following uses the OOC
binary method to write six
f64 values to the file "double.dat". Then this file is read using function
"get_bin".
% set file [open double.dat w]
filee1eb10
% [nap "{1.5 -3 0 2 4 5}"] binary $file
% close $file
% nap "x = get_bin('double.dat', 'f64')"
::NAP::27-27
% $x all
::NAP::27-27 f64 MissingValue: NaN References: 1
Dimension 0 Size: 6 Name: (NULL) Coordinate-variable: (NULL)
Value:
1.5 -3 0 2 4 5
fact
Now let's define a factorial function called "fact". Of course we cannot resist the temptation to
use recursion:
% proc fact n {
if {[$n] > 1} {
nap "n * fact(n-1)"
} else {
nap "1"
}
}
This works fine for scalar arguments:
% [nap "fact 4"] 24 % [nap "fact 1"] 1 % [nap "fact 0"] 1But the following shows that it fails for a vector argument!
% [nap "fact {0 1 4 6}"]
1
factorial
One can define a proper elemental factorial function as follows:
% proc factorial n {
if {[[nap "max(reshape(n)) > 1"]]} {
nap "n > 1 ? n * factorial(n-1) : 1"
} else {
nap "1"
}
}
% [nap "factorial {0 1 4 6}"]
1 1 24 720
Note the double brackets in the if command. The inner brackets
produce an OOC-name. The outer brackets execute this OOC to produce the
string "0" or "1".
a(b)", which is of course equivalent to "a b". NAP checks whether "a" is a Tcl variable. If not, it is assumed to be a
function. In this case NAP first looks for a Tcl procedure called
"::NAP::a." If this does not exist then NAP looks
for a built-in NAP function called "a". If this does not exist then NAP looks for a Tcl
procedure called "a".
The following example shows that a procedure with the global name
"sin" does not override the built-in function with
that name, whereas defining it within the NAP namespace "::NAP::" does override:
% proc sin x {nap "2*x"}
% [nap "sin 1"]
0.841471
% proc ::NAP::sin x {nap "2*x"}
% [nap "sin 1"]
2
It is possible to call some procedures as either functions or
commands. The following example defines and uses the same function
"sind" defined above:
% proc sind degrees {nap "sin(1r180p * degrees)"}
% [nap "sind 30"]; # call as function
0.5
% nap "s = [sind 30]"; # call as command within NAP
expression
::NAP::80-80
% $s
0.5
% [sind 30] all; # call as direct OOC
::NAP::86-86 f64 MissingValue: NaN References: 0
Value:
0.5
But there is a problem calling procedures as commands if the
result is referenced by a variable which is local to the procedure.
At the end of the procedure Tcl deletes such local variables. This
causes the referenced NAOs to be deleted. For example we could
redefine function "sind" as follows:
% proc sind degrees {
nap "result = sin(1r180p * degrees)"
nap "result"
}
% [nap "sind 30"]
0.5
% [sind 30]
invalid command name "::NAP::32-32"
Note that the call as a function still worked but not the call as a command. NAP operates in a special mode while executing a procedure called as a function. The deletion of NAOs referenced by local variables is delayed until after the result has been saved. This is one advantage of calling procedures as functions rather than commands.
write_expr
% proc write_expr {
expr
filename
} {
set channel [open $filename w]
puts $channel [[uplevel nap \"$expr\"] value]
close $channel
}
% nap "to = 5"
::NAP::52-52
% write_expr "1 .. to /// {0 7}" matrix.txt
% cat matrix.txt; # display contents of file 'matrix.txt'
1 2 3 4 5
0 7 0 7 0
get_binary
Next let's define a procedure called "get_binary" which is intended to be called as a
command, but does essentially the same thing as the above function
"get_bin". This will help us to compare the two
techniques in a situation where each has some advantages and some
disadvantages. We assume the file "double.dat" still exists. The following example
defines and tests procedure "get_binary":
% proc get_binary {
filename
{datatype f32}
{swap 0}
} {
set channel [open $filename]
nap "in = [nap_get [lindex {binary swap} $swap] $channel $datatype]"
close $channel
nap "+in"; # Define result as copy of 'in' to
prevent premature deletion
}
% nap "x = [get_binary double.dat f64]"
::NAP::63-63
% $x all
::NAP::63-63 f64 MissingValue: NaN References: 1
Dimension 0 Size: 6 Name: (NULL) Coordinate-variable: (NULL)
Value:
1.5 -3 0 2 4 5
Note that "get_binary" is simpler to define and simpler to use
than "get_bin". The main reason for this is the fact that
all three arguments are used as strings rather than NAOs. One
disadvantage of the command approach is the need to define the result
as "+in" rather than simply "in".