Can anyone help explain to me what the snippet below does. This code snippet is taken from http://wiki.tcl.tk/12374. It is meant to create a tic tac toe game. There are not many resources out there for understanding Tk/Tcl so this is giving me significant difficulty.
proc DrawBoard {{redraw 0}} {
global S B GAME C
if {$redraw} { ;# Must redraw everything
.c delete all
set w2 [expr {$B(w2) - 15}] ;# Make a little margins
set h2 [expr {$B(h2) - 15}]
set hbar [expr {$h2 / 3.0}]
set vbar [expr {$w2 / 3.0}]
set B(0) [list -$w2 -$h2 -$vbar -$hbar] ;# All 9 cells
set B(1) [list -$vbar -$h2 $vbar -$hbar]
set B(2) [list $vbar -$h2 $w2 -$hbar]
set B(3) [list -$w2 -$hbar -$vbar $hbar]
set B(4) [list -$vbar -$hbar $vbar $hbar]
set B(5) [list $vbar -$hbar $w2 $hbar]
set B(6) [list -$w2 $hbar -$vbar $h2]
set B(7) [list -$vbar $hbar $vbar $h2]
set B(8) [list $vbar $hbar $w2 $h2]
for {set i 0} {$i < 9} {incr i} { ;# Rectangle for each cell
.c create rect $B($i) -tag b$i -fill {} -outline {}
.c bind b$i <Button-1> [list DoClick $i]
set B($i) [ShrinkBox $B($i) 25]
}
.c create line -$w2 $hbar $w2 $hbar -tag bar ;# Draw the cross bars
.c create line -$w2 -$hbar $w2 -$hbar -tag bar
.c create line $vbar -$h2 $vbar $h2 -tag bar
.c create line -$vbar -$h2 -$vbar $h2 -tag bar
.c itemconfig bar -width 20 -fill $::C(bars) -capstyle round
}
.new config -state [expr {$GAME(tcnt) == 0 ? "disabled" : "normal"}]
for {set i 0} {$i < 9} {incr i} {
.c itemconfig b$i -fill {} ;# Erase any win lines
DrawXO $GAME(board,$i) $i
}
foreach i $GAME(win) { ;# Do we have a winner???
.c itemconfig b$i -fill $C(win)
}
}
Ok, the most significant question I have regards w2, h2, hbar, vbar variables. Particularly with how they are declared. For instance, set w2 [expr {$B(w2) - 15}]. How can w2 be defined referring to itself??? The author uses these variables to draw the tic tac toe lines, but I don't even know how what these variables mean. Does these variables specify some dimension of the canvas so that the author can use it to bind particular regions to click activities?
If I understand these four variables, everything else will make sense!
Here is the image of what the board looks like:
Variables in Tcl (Tk is just a window drawing toolkit that lives on top of Tcl) are defined when they are written to; there's usually no explicit declaration. The only exception to this is with variables directly in a namespace, where it is best practice to use the variable command to declare them before first use, like this:
namespace eval exampleNamespace {
variable xmpl1 "abc def"
# Or equivalently...
variable xmpl2
set xmpl2 "abc def"
# You have to use the second style with arrays...
variable arrayXmpl
set arrayXmpl(1) "pqr stu"
set arrayXmpl(2) "qwerty uiop"
}
Local variables in procedures don't need declaring, though if you want to access a variable that isn't local you have to use a command (often global or upvar) to bring it into scope.
proc variableExample {formalArgument1 formalArgument2} {
set localVar1 "abc"
set localVar2 "def"
global thisOtherVar
append thisOtherVar "ghi" $formalArgument1
puts "Currently, got $localVar1 $localVar2 and '$thisOtherVar'"
}
It's pretty conventional to put the global at the top of the procedure, but it's totally not necessary. It's effect persists from where you do it until the end of the procedure call. Tcl's semantics are strictly operational with extremely tightly defined evaluation order (it's left-to-right, always).
Now, arrays are aggregate variables. Each element of an array is a variable itself. They're distinct from normal simple variables, though the name of the overall array is in the same naming scheme as the simple variables. You can't have a simple foo and a foo(bar) in the same scope (without unsetting one first, which removes the variable). The keys into the elements are strings — the implementation behind the scenes is a high-performance hash table — that are entirely not variables and this means that the B(w2) and w2 variables are entirely distinct; they're not the same thing at all. However, we can use variables (and other Tcl substitutions) in computing the string to use as a key, so we can do this:
set name "w2"
set B($name) "example of "
append B(w2) "array key handling"
puts "this is an $B($name)"
Let's look at the example that you were puzzling over:
set w2 [expr {$B(w2) - 15}]
Breaking it into pieces:
set w2 […]
It's going to write to the variable w2. That's what the set command does with two arguments. The thing that is going to be written in is the result of evaluating another command. We need to look deeper.
expr {$B(w2) - 15}
Breaking it into pieces:
expr {…}
That yields the result of evaluating the expression in the braces. It's strongly recommended that you put braces around all your expressions; it's safer and much faster. What's the expression? (Note that expressions use a different syntax to the rest of Tcl.)
$B(w2) - 15
OK, that's subtracting 15 (the number) from the value read (because of the $) from the w2 element of the array B. The w2 here is just a string. That there's a variable elsewhere with the same name is coincidence.
And that's it. Reassembling the pieces, we see that:
set w2 [expr {$B(w2) - 15}]
Assigns the result of subtracting 15 from the contents of B(w2) to the variable w2. That's all it does. (The array B is a global array; see the global at the top of the procedure.)
The lines:
set w2 [expr {$B(w2) - 15}] ;# Make a little margins
set h2 [expr {$B(h2) - 15}]
set hbar [expr {$h2 / 3.0}]
set vbar [expr {$w2 / 3.0}]
These get the half height and width of the canvas from the global array B, remove 15 pixels for a margin, and then set hbar/vbar to a third of that value so that the coordinates for drawing are easier. It helps once you realize that the canvas has had its drawing origin moved (similar to in scrolling) to the center of its window. And be aware that doing -$hbar is actually being a bit naughty, though cute; it's using string concatenation to negate a value, which is OK when the value is positive and doesn't have an explicit sign, but would be fragile if that ever changed. And it's slow by comparison with doing [expr {-$hbar}], though that's longer; the cost of computation isn't exactly matched with the length of the command.
Related
I am new to TCL and hardware design. I am currently doing a script that could verify all the ports at an edge and make a rectangle box that cover all the ports at an edge.
Since I am new to TCL, I think there is something wrong in the syntax.
The error msg is:
Error: Invalid coordinates '$bbox_coor_x1 [expr $bbox_coor_y2-30]' in list. (NDMUI-100)
Error: Empty or invalid geometry specified for the '-boundary' argument of the command. (NDMUI-337).
Please help me.
Your problem is that variables and commands are not interpolated inside curly braces. Please learn the difference between curlies and double quotes.
For example:
set x 5
set y 10
# Curlies
puts {$x $y}
--> $x $y (literally)
puts {$x [expr $y+1]}
--> $x [expr $y+1] (literally)
# Double quotes
puts "$x $y"
--> 5 10
# List command
puts [list $x $y]
--> 5 10
puts [list $x [expr $y+1]]
--> 5 11
When making a list of lists, like a bbox, anything inside outer-most curlies will interpolate:
puts "{$x $x} {$y $y}"
--> {5 5} {10 10}
One more thing, note that lindex can take multiple indexes. Do this instead of calling lindex twice.
lindex $bbox 0 1
In following code in TCL I cannot seem evaluate the variable "a"
I'm evaluating x and y, in the same For Loop I have a IF statement that is checking for a range between x and y.
If valid then I'd like to perform some more calculations within the IF condition.
Every thing is fine up to the IF condition, but I cant seem to evaluate "a".
I'm trying to set "a" to the value of "y" for all the values within the range $min <= $x && $x <= $max
I would kindly request the experts to highlight the mistake.
for {set i 0} {$i < $lenght} {incr i} {
set x [expr ([lindex $cal1 $i])*$offset]
set y [expr ((cal2)/2) ]
if {$min <= $x && $x <= $max } {
puts "is Active"
set a [lindex $y $i]
puts a = $a
}
}
There is a lot that seems problematic in your code.
In the first line, you use the variable lenght. Tcl doesn't care about spelling, but if you don't have such a variable (you might possibly have a length variable instead) you will get an error.
In the invocation expr ([lindex $cal1 $i])*$offset] you have an unnecessary parenthesis but no braces around the expression (the braces aren't mandatory but should be there unless there is a very good reason to omit them). Also: "offset" usually means something you add to, not multiply with, another value. The invocation expr {[lindex $cal1 $i] * $offset}] would be better.
The variable y is used as a list argument to lindex later on, but it's created as a scalar variable. Also, your expression divides a string (or rather, an invalid bareword) with 2. Maybe you meant lappend y [expr {$cal2 / 2}]? If you use lappend, each value will be added to the end of an existing list, or as the first element of a new list if y doesn't exist. This is usually what one wants, but it means that the list y should be reset using set y [list] or set y {} before entering the loop, to get rid of elements added earlier, if any.
puts a = $a won't work, because if there are more than one argument to puts they are expected to be the flag -nonewline and/or a channel id to send the output to. Maybe you meant puts "a = $a".
i am new to ns2.
In the following code
for {set i 0 }{$i < $val(nn)}{incr i }
{
set n$i [$ns node]
$n$i set X_[expr 10+round(rand()*40)]
}
I want to create 50 nodes but while executing the tcl file it shows the error as "invalid command name "$n0" while executing "$n$i set X_[expr 10+round(rand()*40)]". I have provided nn to be 50.
You've got two classes of problems with the code you posted.
Basic syntax problems
for {set i 0 }{$i < $val(nn)}{incr i }
{
This won't work because you need spaces between arguments to for (yes, for isn't a keyword in Tcl; it's just a regular command) and you must put the start of the body at the end of the line. You are strongly recommended to use One True Brace style coding; that's what Tcl's syntax makes most easy.
Here's how to fix that:
for {set i 0} {$i < $val(nn)} {incr i} {
Note that all I've done to fix it is move around (and insert) spaces.
Deeper syntax problems
set n$i [$ns node]
$n$i set X_[expr 10+round(rand()*40)]
These lines have a problem in that you're trying to use double substitution. Well sort of. The $ handling is really dumb, in that it checks for a non-empty “nice” variable name afterwards, substituting it if it is there and becoming a plain old $ otherwise. This means that $n$i is the concatenation of what you get from $n and $i.
The easiest fix is to use another variable as well:
set thisnode [$ns node]
set n$i $thisnode
$thisnode set X_[expr 10+round(rand()*40)]
You can even roll those first two lines into one, since the result of set is the value that was just set:
set n$i [set thisnode [$ns node]]
$thisnode set X_[expr 10+round(rand()*40)]
But we really ought to encourage you to use arrays for this sort of thing, as they do allow double-substitution of a sort.
set n($i) [$ns node]
$n($i) set X_[expr 10+round(rand()*40)]
Those extra (…) make a big difference!
You could also use a list to store the generated items:
lappend n [$ns node]
[lindex $n end] set X_[expr 10+round(rand()*40)]
But I'd really be tempted to use a helper variable in that case:
lappend n [set thisnode [$ns node]]
$thisnode set X_[expr 10+round(rand()*40)]
Other things
You also ought to put your expression in curly braces. It doesn't matter in this particular case as it's not got any substitutions in it, but it's an extremely good habit to get into as it makes it much easier for Tcl to compile the expression in general; i.e., when the overall expression is a literal, Tcl can compile it ahead of time and that's fast, whereas when the expression is non-literal it can't be compiled until the last moment, has to be compiled each time through the loop, and can have a whole bunch of other hazards too. It would make a much larger difference if you were doing a loop over millions of things rather than 50…
It might be worth factoring the expression into a little procedure, for clarity:
proc random {from to} {
expr {$from + round(rand() * ($to-$from))}
}
$n($i) set X_[random 10 50]
Write it (and debug it!) once and reuse it elsewhere. It's the Lazy Programmers' Way.
I'm guessing that this is really an NS2 question too. In which case you'll probably run into problems still: the underscore between the X and the [expr …] is a little surprising, unless you really have got objects with large numbers of very similar variables where you want to read (and then discard the value of) a random one of them. That would be… rather surprising, yes? I suspect you might be better off with:
for {set i 0} {$i < $val(nn)} {incr i} {
set n($i) [$ns node]
$n($i) set X [expr {10+round(rand()*40)}]
}
Do you need to keep the references around? If not, you might even be OK with:
for {set i 0} {$i < $val(nn)} {incr i} {
[$ns node] set X [expr {10+round(rand()*40)}]
}
But that that's probably not what you want though; in real code you'll be wanting to refer to the nodes from elsewhere so you can put in links between them…
Tcl works with whitespace-separated words (See http://tcl.tk/man/tcl8.5/TclCmd/Tcl.htm rules 1,2,3), and the syntax of the for command is
for start test next body
http://tcl.tk/man/tcl8.5/TclCmd/for.htm
You need this:
for {set i 0} {$i < $val(nn)} {incr i} {
set n$i [$ns node]
[set n$i] set X_[expr {10+round(rand()*40)}]
}
Brain Teaser: I self originated this question, but stuck completely.
I want to create all possible combination of all characters, but of all possible lengths. Suppose, [a-z] combination of 1 length, then [a-z] combination of 2 length, and so on till the maximum length achieved.
this could be very easily done by iterative looping.
Example for 3 length:
proc triples list {
foreach i $list {
foreach j $list {
foreach k $list {
puts [list $i $j $k]
}
}
}
}
But, it should solve using less loops (looping needs to be dynamic)
set chars "abcdefghijklmnopqrstuvwxyz"
set chars [split $chars ""]
set complete_length [llength $chars]
set start 0
set maximum_length 15
while {1} {
if {$start > $maximum_length} {
break
}
for {set i [expr $maximum_length-$start]} {$i >= 0} {incr i -1} {
# dump combinations
}
incr start
}
In this chunk, what algorithm or method i should apply? Any kind of suggestions/help/code will be appreciated.
Sry, this is not an answer, but hopefully some interesting discussion anyway:
The word "combinations" is often used way too generally, so it can be interpreted in many different ways. Let's say that you have a source list of 26 different elements, the english letters, and you want to pick 3 of them and combine in a 3 element destination list:
Can you always pick any letter from the source list, or do the elements disappear from it as you pick them? Either define "pick" (are the elements copied or moved during a pick), or define the set of source values (is there 1 of each of A-Z or an infinite amount of A-Z).
Does the order in the destination list matter? Is AHM considered to be the same combination as HAM? Define "combine".
If you have a list where not all elements are different, e.g. {2 10 10 64 100}, you have even more possibilities. Define your set of values.
Your first example prints permutations, not combinations. If that's what you want, the easiset way is a recursive procedure. Combinations are more complicated to generate.
EDIT:
I wrote this procedure for a Project Euler program. It picks all the elements, but maybe you can modify it to pick n. It takes a command prefix as argument, so you don't have to store all permutations.
package require Tcl 8.5.0
proc forEachPerm {list cmdPrefix} {
_forEachPerm {} $list $cmdPrefix
}
proc _forEachPerm {head list cmdPrefix} {
if {![llength $list]} {
{*}$cmdPrefix $head
} else {
for {set i 0} {$i < [llength $list]} {incr i} {
_forEachPerm [concat $head [lrange $list $i $i]] [lreplace $list $i $i] $cmdPrefix
}
}
}
# example use:
forEachPerm {a b c} {apply {{list} {puts [join $list]}}}
Hi i have a integer value and based on the value i have to create the entry boxes in a tcl/tk gui. So i did something like this:
set frame1 [::hwt::LabeledFrame [::hwt::WindowRecess editThicknessWindow].frame1 "Current List: " \
-expand 0 -relief flat -anchor e -side top -pady 15]
for {set i 0} {$i < $length_Thickness} {incr i} {
set Entry_No_$i [::hwt::AddEntry $frame1.Entry_No_[eval $i] label "List Values_$i :" labelwidth 15 \
entryWidth 10 anchor nw validate real text [namespace current]::arr_attribOptionsValue(Thickness)[$i] state normal \
withoutPacking -textvariable [namespace current]::lst_Value[$i] ]
pack $Entry_No_[eval $i] -side top -anchor nw -padx 10 -pady 10
}
So let's say i have the integer number as 3 then i have to create 3 entry boxes. I have a list "arr_attribOptionsValue(Thickness)" with 3 values in it so i want the values from the list to be populated in the entryboxes. Since i am new to tcl/tk so not sure if the syntax is correct or if i am missing something. I am confused like i am creating the tk variable Entry_No_$i and in the pack i am using $Entry_No_[eval $i] so will these two refer to the same value or is it wrong syntax.
Firstly, if you're creating variables like $Entry_No_[eval $i] then I really think you're going about it the wrong way. Such compound variables are really much more easily done as array elements, such as Entry_No($i), with no eval in there to confuse things (or $Entry_No($i) to read from the element instead of name it).
Secondly, [namespace current]::arr_attribOptionsValue(Thickness)[$i] is even more likely to be wrong, especially as $i will be an integer. You can't address an element of a list like that (it't been suggested that it ought to be possible, but that's not going to happen in the next few months for sure, and certainly not in combination with an array like that). Instead, the simplest mechanism is to use another array that you populate from the source list and to use a trace to couple back if necessary. (Traces are a more advanced technique; ask another question if you need them.) Populating the working array from list might be done like this:
set i 0
foreach item $arr_attribOptions(Thickness) {
set varname arr_attribOptions(Thickness,$i)
set $varname $item
incr i
}
Yes, you can store a variable name in a variable. (When reading, use [set $varname] to do the double dereference.)
Thirdly, widget names are best if they don't contain most non-alphanumeric characters (except ., of course) and don't start any component with a capital letter (for technical reasons relating to window classes).
Fourthly, please use variable (or upvar or namespace upvar) to avoid having to use fully-qualified variables.
Overall, we can use these techniques together to get something like this:
set frame1 [::hwt::LabeledFrame [::hwt::WindowRecess editThicknessWindow].frame1 "Current List: " \
-expand 0 -relief flat -anchor e -side top -pady 15]
namespace upvar [namespace current] \
arr_attribOptions attribs arr_values values lst_Value valueList
set i 0
foreach item $attribs(Thickness) {
set attribs(Thickness,$i) $item
set values($i) [lindex $valueList $i]
incr i
}
for {set i 0} {$i < $length_Thickness} {incr i} {
set Entry_No($i) [::hwt::AddEntry $frame1.entry_No_$i label "List Values_$i :" labelwidth 15 \
entryWidth 10 anchor nw validate real text arr_attribOptions(Thickness,$i) state normal \
withoutPacking -textvariable [namespace current]::arr_values($i)]
pack $Entry_No($i) -side top -anchor nw -padx 10 -pady 10
}
I don't know that I've identified all the problems yet (and most Tcl programmers don't use Hungarian type prefixes on variable names) but it is a lot closer to idiomatic now.