Tcl provides a way of creating new commands, called procedures, that can be executed in scripts. The arguments of a procedure can be call-by-value or call-by-reference, and there is also a facility for creating new user defined control structures using procedures.
A procedure is declared using the proc
command:
proc name argList body
where the name of the procedure is name, the arguments are contained in argList and the body of the procedure is the script body. An example of a procedure is:
proc namePrint { first family } { puts "My first name is $first" puts "My family name is $family" }
which can be called with
namePrint Tony Blair
to produce the output:
My first name is Tony My family name is Blair
A procedure with no arguments is specified with an empty argument list. An example is a procedure that just prints out a string:
proc stringThing {} { puts "I just print this string" }
Arguments can be given defaults by pairing them with a value in a list. An example here is a counter procedure:
proc counter { value { inc 1 } } { eval $value + $inc }
which can be called with two arguments like this
set v 10 set v [counter $v 5]
which will set variable v
to the value 15; or it can be called
with one argument:
set v 10 set v [counter $v]
in which case v
will have the value 11, because the default of
the argument inc
inside the procedure is the value 1.
There is a special argument for handling procedures with variable
number of arguments, the args
argument. An example
is a procedure that sums a list of numbers:
proc sum { args } { set result 0; foreach n $args { set result [expr $result + $n ] } return $result; }
which can be called like this:
sum 1 2 3 4 5
which returns the value 15.
The restriction on using defaulted arguments is that all the
arguments that come after the defaulted ones must also be
defaulted. If using args
then it must be the last argument
in the argument list.
A procedure can return a value through the return
command:
return ?options? ?value?
which terminates the procedure returning value value, if specified, or just causes the procedure to return, if no value specified. (The ?options? part has to do with raising exceptions, which we will will not cover here.)
The return result of a user defined procedure is the return result of the last command executed by it.
So far we have seen the arguments of a procedure are passed using
the call-by-value mechanism. They can be passed call by reference using
the upvar
command:
upvar ?level? otherVar1 myVar1 ?otherVar2 myVar2 ...?
which makes accessible variables somewhere in a calling context with the current context. The optional argument level describes how many calling levels up to look for the variable. This is best shown with an example:
set a 10 set b 20 proc add { first second } { upvar $first f $second s expr $f+$s }
which when called with
add a b
will produce the result 30. If you use call-by-value instead:
add $a $b
then the program will fail because when executing the procedure
add
it will take the first argument 10 as the level
argument, a bad level. (Also variable 20
doesn't exist at
any level.)
New control structures can be generated using the uplevel
command:
uplevel ?level? arg ?arg arg ...?
which is like eval
, but it evaluates its arguments in a
context higher up the calling stack. How far up the stack to go is given
by the optional level argument.
proc do { loop condition } { set nostop 1 while { $nostop } { uplevel $loop if {[uplevel "expr $condition"] == 0} { set nostop 0 } } }
which when called with this
set x 5 do { puts $x; incr x -1 } { $x > 0 }
will print
5 4 3 2 1
(Please note: this doesn't quite work for all kinds of calls because of
break
, continue
, and return
. It is possible to get
around these problem, but that is outside the scope of this tutorial.)