Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I efficiently (mem/time) modify all elelements of a list in Tcl?

To operate on each element of a list, returning a modified list various languages have explicit constructs.

In Perl there's map:

perl -e 'my @a = (1..4); print join(q( ), map { $_ * $_ } @a)'
1 4 9 16

In Python there're list comprehensions:

>>> a = (1,2,3,4)
>>> [el*el for el in a]
[1, 4, 9, 16]

What's the most efficient way to do this in Tcl? I can come up with the usual foreach loop.

set l {}
foreach i {1 2 3 4} {
    lappend l [expr $i * $i]
}
puts $l
1 4 9 16

Is this the fastest way?

Regarding mem efficiency this builds up a second list, one by one. If I don't need the list permanently is there a more efficient way?

And, finally, is there something that's shorter? I couldn't find infos here or in the http://wiki.tcl.tk

Answer:

As Donal Fellows has answered, most importantly for speed tests, things should be wrapped in a proc {} since Tcl then can optimize. For Tcl, a "map" function is discussed as a future enhancement. With this hint and further searching I found http://wiki.tcl.tk/12848

like image 488
cfi Avatar asked Oct 11 '11 08:10

cfi


2 Answers

The most efficient method is this:

set idx 0
foreach item $theList {
    lset theList $idx [expr {$item * $item}]
    incr idx
}

If the list is short (e.g., a few hundred elements) the cost of allocating a new list is minimal though, so you can use this (simpler) version instead:

foreach item $theList {
    lappend newList [expr {$item * $item}]
}

Note that the foreach command is only fast if placed in a procedure (or lambda expression or method) and expressions are only fast if placed in {braces}. Also, don't speculate, measure: take care to use the time command to find out how fast your code really is.

like image 180
Donal Fellows Avatar answered Sep 21 '22 02:09

Donal Fellows


Well, there is something shorter (using the tcllib struct::list package), but not necessarily faster.

package require struct::list
puts [struct::list mapfor x $data { expr {$x * $x} }]
like image 26
schlenk Avatar answered Sep 20 '22 02:09

schlenk