Reputation: 1716
When using foreach
in TCL to loop through a list it is desired to have a running number of the index of the current object. A way I have done this before is to maintain an extra counter variable.
set ct 0
foreach x $list {
puts "Items is $x index is $ct"
incr ct
}
One could use lsreach to retrieve the index but that's compute intensive and could be problematic with double occurrences.
Wondering if there is streamlined sleek-looking way of maintaining index information during a loop.
Pseudocode :
foreach x $list {
puts "Items is $x index is [foreach_index $x]"
}
Your feedback is appreciated.
UPDATE:
Run time tests with the provided answers:
Loop through list with 100k random strings 80char long. The loop proc is fastest, but hard to read.
Upvotes: 2
Views: 8234
Reputation: 13252
While the control structure definitions in other languages are the law, in Tcl they're more like a set of guidelines.
proc foreachWithIndex {indexVar args} {
upvar 1 $indexVar var
set var 0
uplevel 1 [list foreach {*}[lrange $args 0 end-1] "[lindex $args end];incr $indexVar"]
}
foreachWithIndex x v {a b c} {puts "$x: $v"}
But I suggest using for
instead. Radical language modifications are fun and occasionally useful, but if I had an Imperial credit for every such clever construct I ended up throwing away later I could build my own Death Star and still have money to put a grating over the exhaust port.
Upvotes: 4
Reputation: 71
Your method of using incr
with a counter works ok. This also works:
for { set i 0 } { $i < [llength $list] } { incr i } {
set x [lindex $list $i]
puts "Items is $x index is $i"
}
Another advantage of doing it this way is that you can modify the list while you are iterating. Let's say you want to remove all items with the value "bad" from a list.
set values { 1 2 3 bad 4 bad bad 5 }
for { set i 0 } { $i < [llength $values] } { incr i } {
if { [lindex $values $i] eq "bad" } {
set values [lreplace $values $i $i]
incr i -1
}
}
puts $values
Upvotes: 3