Tcl can't read variable: no such variable

2019-07-25 05:08发布

问题:

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
}

回答1:

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.



回答2:

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
}


标签: tcl ns2