Depends on what you mean by "more efficient." And how much time you've already got invested in the approach you're taking (as well as the amount of code you'd need to change if you changed the API). If it works fine the way it is, you might want to leave it
almost untouched. But if it's running a bit too slow, I do have some tips for speeding things up, which I'll get to in a minute.
But how would I handle this situation if I were tackling it fresh? Well, my first step always is to see if someone else has already done the work for me by creating a Tcl extension or other "drop-in" piece of code. Whenever possible, I prefer to let someone else deal with maintenance hassles.

The Tcler's Wiki,
(or equivalently,
is a great place to start your search, as is the Tcl Developer's Xchange,
although that site is starting to suffer a bit of link rot.
In this case, the Wiki provided information on
tcllib, the standard Tcl library, which includes a basic math package (described at
but it provides only basic functions like max, min, and simple statistics.
More applicable to your needs, though, is
La, The Hume Linear Algebra Tcl Package. The full description and download is available at
La is a pure Tcl extension, which is good because it is completely platform-independent and easy to install, and which is bad because it can be slow for lots of manipulation of big matrices.
A disadvantage in your case of moving to an extension like La is that the API is difference from what you've developed. Is it worth the hassle of reimplementing your application(s)? Perhaps not. So, let's take a look at your approach with your sample procedure.
One of the first things I noticed is that you defined your procedure to accept two arrays as arguments (passing them by reference using
upvar), but return a list. If possible, I would probably design a library so that I could maintain a consistent data format, representing data as lists only or as arrays only. Personally, I tend to prefer lists in a case like this because they are "first-class objects" in Tcl: I can pass their values to and from procedures, copy the data into other variables, and otherwise manipulate them easily without needing to turn to
upvar tricks in my code. Arrays are useful, and are still a little faster than lists for data access in most cases, but aren't "first-class objects," so you need to use
upvar to pass them to and from procedures, and there's no efficient way to copy an array (
array get/array set is your only option).
If you wanted to modify your procedure so that you could return an array directly, you would just need to add a third parameter to your
proc declaration to pass the name of a third array variable, and then use another
upvar to access it from within your procedure. For example:
Code:
proc crossP {input1 input2 output} {
upvar $input1 x
upvar $input2 y
upvar $output z
# Overwrite output if it already exists
catch {unset z}
set z(1) [expr $x(2)*$y(3) - $x(3)*$y(2)]
set z(2) [expr $x(3)*$y(1) - $x(1)*$y(3)]
set z(3) [expr $x(1)*$y(2) - $x(2)*$y(1)]
}
crossP a b result
Note that this still works even if the output array variable doesn't exist when you call your procedure.
upvar ensures that the variable will be created in the proper scope.
What about making the implementation you have more efficient? Well, I still have a suggestion even there...
When using
expr, always quote your argument in {}.
expr does its own round of variable and command substitutions (in addition to the round of substitutions performed by the Tcl interpreter). Quoting your math expression in {} prevents Tcl from doing the substitutions and lets
expr take care of it instead. So we've cut the overhead of one round of substitutions. Also,
expr is able to parse and compile more efficiently an expression quoted with {} versus an unquoted expression, just because the way the parser is written. The difference is dramatic, sometimes an order of magnitude depending on how complex the expression is. (There are various technical reasons why
expr performs this extra round of substitutions, which we won't get into here.) There are very few instances where you don't want to quote the expression with {}. I think you'll be able to figure them out if you ever encounter them on your own.
So here's a sample implementation that makes only that change:
Code:
proc crossP2 {a b} {
upvar $a x
upvar $b y
set z(1) [expr {$x(2)*$y(3) - $x(3)*$y(2)}]
set z(2) [expr {$x(3)*$y(1) - $x(1)*$y(3)}]
set z(3) [expr {$x(1)*$y(2) - $x(2)*$y(1)}]
return [array get z]
}
Let's run some timing experiments to see how much this improved performance:
[tt]%
array set a {1 414.2 2 -3.1415 3 7.6}
%
array set b {1 -395.96 2 -0.76 3 219.15}
%
time {crossP a b} 10000
253 microseconds per iteration
%
time {crossP2 a b} 10000
48 microseconds per iteration[/tt]
Of course, if you're doing really intensive number crunching, Tcl might be too slow. In which case, you might need to implement your procedures in C, but it's always a bit of a hassle to mix languages like that, and it makes your code less portable.
I hope you found this helpful. And if I may include a bit of a personal plug here: If people have found my contributions to this forum useful, I would certainly appreciate any referrals you could provide. Avia offers not only training in Tcl/Tk and Expect, but also consulting services (short- and the long-term). So if you've benefited from the few tips and tricks I've managed to post here in my spare time, just imagine what it would be like to get my undivided attention for a few days.
Thanks. - Ken Jones, President
Avia Training and Consulting
866-TCL-HELP (866-825-4357) US Toll free
415-643-8692 Voice
415-643-8697 Fax