I want to create array dynamically and assign value to it. e.g.
set num 0
foreach i {1 2 3} {
set count$i(sp) {$num}
incr num
set count$i(ep) [expr $num 1]
}
CodePudding user response:
When I ran your sample code, this error comes up:
can't read "i(sp)": variable isn't array
which is because the $i(sp) part of count$i(sp) looks like a reference to an existing array i.
You'll want to isolate $i from (sp) with curly brackets:
set num 0
foreach i {1 2 3} {
set count${i}(sp) {$num}
incr num
set count${i}(ep) [expr $num 1]
}
which creates three arrays:
tcl8.6.8> parray count1
count1(ep) = 2
count1(sp) = $num
tcl8.6.8> parray count2
count2(ep) = 3
count2(sp) = $num
tcl8.6.8> parray count3
count3(ep) = 4
count3(sp) = $num
Note above that curly brackets around $num use the literal string $num.
You'll want to remove those curly brackets:
set num 0
foreach i {1 2 3} {
set count${i}(sp) $num
incr num
set count${i}(ep) [expr $num 1]
}
tcl8.6.8> parray count1
count1(ep) = 2
count1(sp) = 0
tcl8.6.8> parray count2
count2(ep) = 3
count2(sp) = 1
tcl8.6.8> parray count3
count3(ep) = 4
count3(sp) = 2
However, please explain exactly what you mean by "dynamically creating a Tcl array". Do you really want three separate arrays or could you have one array with more names, like this:
set num 0
foreach i {1 2 3} {
set count($i,sp) $num
incr num
set count($i,ep) [expr $num 1]
}
tcl8.6.8> parray count
count(1,ep) = 2
count(1,sp) = 0
count(2,ep) = 3
count(2,sp) = 1
count(3,ep) = 4
count(3,sp) = 2
CodePudding user response:
You might enjoy using a dict instead of an array:
set count {}
set num 0
foreach i {1 2 3} {
dict set count $i sp $num
incr num
dict set count $i ep [expr {$num 1}]
}
set count ;# -> 1 {sp 0 ep 2} 2 {sp 1 ep 3} 3 {sp 2 ep 4}
dict get $count 3 ep ;# -> 4
CodePudding user response:
If you can avoid using a dynamic variable name like that, do so; they're often more trouble than they're worth. (You can use “composite” element names like $i,sp instead.) Otherwise — perhaps because of other code that accesses the array — the simplest way of handling them is to use upvar to make an alias to the variable with a fixed name. In particular, upvar 0 makes an alias to a variable in the same scope:
set num 0
foreach i {1 2 3} {
upvar 0 count$i ary
set ary(sp) {$num}
incr num
set ary(ep) [expr $num 1]
}
The only problem with upvar 0 is that you can't really undo it; that's why you almost always only use it within a procedure so the aliasing goes away naturally when the procedure exits.
Variable aliases are extremely efficient within the context of a procedure, substantially more so than using compound variable names in multiple places.
Also, {$num} looks suspicious. It's not wrong code in that it has a correct interpretation, but might not do what you expect and is often indicative of a problem, as $num isn't substituted. You might prefer [list $num].
