The following piece of code produces the error:
can't read "n": no such variable
while executing
"$ns duplex-link $n$i $n([expr ($i+1)%120]) 1Mb 10ms DropTail"
("for" body line 2)
invoked from within
"for {set i 7} {$i < 120} {incr i} {
$ns duplex-link $n$i $n([expr ($i+1)%120]) 1Mb 10ms DropTail
}"
(file "multicast.tcl" line 44)
it seems $n$i is not evaluated to the required format of $n7 etc. Any help in the solution is much appreciated.
for {set i 0} {$i < 120} {incr i} {
set n$i "[$ns node]"
global n$i
}
# Create links
$ns duplex-link $n0 $n1 1.5Mb 10ms DropTail
$ns duplex-link $n0 $n2 1.5Mb 10ms DropTail
$ns duplex-link $n2 $n3 1.5Mb 10ms DropTail
$ns duplex-link $n2 $n4 1.5Mb 10ms DropTail
$ns duplex-link $n1 $n7 1.5Mb 10ms DropTail
$ns duplex-link $n1 $n5 1.5Mb 10ms DropTail
$ns duplex-link $n4 $n6 1.5Mb 10ms DropTail
#create the rest of the links
for {set i 7} {$i < 120} {incr i} {
$ns duplex-link $n$i $n([expr ($i+1)%120]) 1Mb 10ms DropTail
}
Tcl's $
syntax does not parse non-alphanumeric variable names (with a few exceptions I'll come to in a moment) so it stops trying to parse the first part of $n$i
after the n
. This is a limitation of the parser but Tcl itself allows almost anything in.
One of those exceptions is that ::
namespace separators are allowed too, and another is that you can put a complex literal variable name in {
braces}
, like ${n$i}
. That's not helpful here though, as you can't substitute variables into variable names that way.
What you should do
Use an array. The form $somename(stuff-to-do-an-index)
allows a full range of substitutions in the stuff-to-do-an-index
, except for some restrictions on parentheses that hardly ever matter.
global ni; # <-- you might not need this!
for {set i 0} {$i < 120} {incr i} {
set ni($i) "[$ns node]"
}
# Create links
$ns duplex-link $ni(0) $ni(1) 1.5Mb 10ms DropTail
$ns duplex-link $ni(0) $ni(2) 1.5Mb 10ms DropTail
$ns duplex-link $ni(2) $ni(3) 1.5Mb 10ms DropTail
$ns duplex-link $ni(2) $ni(4) 1.5Mb 10ms DropTail
$ns duplex-link $ni(1) $ni(7) 1.5Mb 10ms DropTail
$ns duplex-link $ni(1) $ni(5) 1.5Mb 10ms DropTail
$ns duplex-link $ni(4) $ni(6) 1.5Mb 10ms DropTail
#create the rest of the links
for {set i 7} {$i < 120} {incr i} {
$ns duplex-link $ni($i) $n([expr ($i+1)%120]) 1Mb 10ms DropTail
}
Other alternatives
You can use the one-argument version of set
to read from variables (it's documented, but a little obscure to those new to Tcl).
$ns duplex-link [set n$i] $n([expr ($i+1)%120]) 1Mb 10ms DropTail
You can also use upvar 0
to make an alias to the variable which you can then manipulate normally:
upvar 0 n$i myAlias
$ns duplex-link $myAlias $n([expr ($i+1)%120]) 1Mb 10ms DropTail
Even more ugly would be this construction with subst
:
$ns duplex-link [subst "\$n$i"] $n([expr ($i+1)%120]) 1Mb 10ms DropTail
After that, it gets really nasty with eval
and return -level 0
(which is actually efficient: strange but true!) and all sorts of stuff like that, but really don't go that way. Arrays are perfect for this sort of thing, really.
For clarity, I would introduce some temp vars:
for {set i 7} {$i < 120} {incr i} {
set node1 n$i
set node2 n[expr {($i + 1)%120}]
$ns duplex-link [set $node1] [set $node2] 1Mb 10ms DropTail
}
An invocation of set with only a single arg (the name of a var) returns the value of that variable.
Alternatively, you could use the subst command:
for {set i 7} {$i < 120} {incr i} {
set node1 n$i
set node2 n[expr {($i + 1)%120}]
$ns duplex-link [subst $$node1] [subst $$node2] 1Mb 10ms DropTail
}