Passing back multiple results in Tcl - upvar and uplevel
Archive - Originally posted on "The Horse's Mouth" - 2011-09-01 17:13:55 - Graham EllisWhat's the effect of having a meal? There are many. "You're not hungry any more" is the obvious answer, but also "there's washing up to do", "there's a gap on the shelves" and so on.
And yet when programming with an assignment statement (or a set command in Tcl - I am running a Tcl course this week) there's just a single returned value that is saved to a resut variable.
In Tcl, multiple values could be returned in a list just as in other languages they could be returned in a list / array / hash / tuple / object / dictionary, but Tcl also lets you pass in the names of extra return variables to your proc and then access them within a proc via upvar, or even reach out to variables in the calling code via uplevel.
How does this work?
I wrote an example on yesterday's course - source code [here] - where I was working out how many widgets I could buy for a certain amount of money. The return value was to be the quantity, but I wanted to set a subsiduary result too from which the calling code could ascertain how much money was left. The call:
set stocklevel [buy $each $cashinhand cashleft]
puts "You can buy $stocklevel and you will have $cashleft still to spend"
Note that the target variable for the seond return has no $ in front of it. You're passing in the name of the variable, so that you can declare your proc as follows:
proc buy {unit money_in change} {
upvar $change payback
which says "The variable who's NAME is passed in in the "change" variable from the calling code is to have another name - an alias - of "payback" in the code of the proc. So that when I set payback:
set payback [format %.2f [expr $money_in - $unit * $afford]]
I am really setting the variable named in $change in the calling code ... and that's the variable in that calling code called "cashleft".
There's an alternative - which I'm mentioning here for completeness - the uplevel command. With uplevel, you can request that a piece of code within a proc is performed in the variable namespace of the parent. So another way of returning the second value is to set it directly in the "calling scope":
uplevel "set cashleft $payback"
Under most circumstances, upvar is going to give you better flexibility, though, as the calling code will be able to specify a different variable name each time the proc is called rather than being hardwired, and harder to maintain. There is, however, a new full example of uplevel [here] on the web site.