任何人都可以帮我解释一下下面的代码片段。此代码段取自http://wiki.tcl.tk/12374。它旨在创造一个tic tac toe游戏。没有太多资源可以理解Tk / Tcl,所以这给我带来了很大的困难。
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)
}
}
好的,我关注的w2, h2, hbar, vbar
变量是最重要的问题。特别是他们如何宣布。例如,set w2 [expr {$B(w2) - 15}]
。 w2
如何定义自己?作者使用这些变量来绘制tic tac toe线,但我甚至不知道这些变量是什么意思。这些变量是否指定了画布的某些维度,以便作者可以使用它来绑定特定区域以单击活动?
如果我理解这四个变量,其他一切都有意义!
以下是电路板的图像:
答案 0 :(得分:3)
Tcl中的变量(Tk只是一个生成在Tcl之上的窗口绘图工具包)在写入时被定义;通常没有明确的声明。唯一的例外是直接在命名空间中使用变量,最好在第一次使用之前使用variable
命令声明它们,如下所示:
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"
}
过程中的局部变量不需要声明,但是如果要访问非本地变量,则必须使用命令(通常为global
或{{1将它带入范围。
upvar
将proc variableExample {formalArgument1 formalArgument2} {
set localVar1 "abc"
set localVar2 "def"
global thisOtherVar
append thisOtherVar "ghi" $formalArgument1
puts "Currently, got $localVar1 $localVar2 and '$thisOtherVar'"
}
放在程序的顶部是很常见的,但这完全没有必要。它的效果会持续到您执行此操作的位置,直到过程调用结束。 Tcl的语义严格可操作,具有非常严格定义的评估顺序(从左到右,始终)。
现在,数组是聚合变量。数组的每个元素本身就是一个变量。它们与普通的简单变量不同,尽管整个数组的名称与简单变量的命名方案相同。你不能在同一范围内有一个简单的global
和一个foo
(首先没有foo(bar)
一个,这会删除变量)。元素中的键是 strings - 幕后实现是一个高性能的哈希表 - 完全不是变量,这意味着unset
和B(w2)
个变量是完全不同的;他们根本不是一回事。但是,我们可以使用变量(和其他Tcl替换)来计算要用作键的字符串,因此我们可以这样做:
w2
让我们来看看你困惑的例子:
set name "w2"
set B($name) "example of "
append B(w2) "array key handling"
puts "this is an $B($name)"
将其分解成碎片:
set w2 [expr {$B(w2) - 15}]
它将写入变量set w2 […]
。这就是w2
命令对两个参数的作用。将要写入的内容是评估另一个命令的结果。我们需要深入了解。
set
将其分解成碎片:
expr {$B(w2) - 15}
这会产生评估大括号中表达式的结果。 强烈建议您在所有表达式周围添加大括号;它更安全,更快。表达是什么? (注意,表达式使用与Tcl其余部分不同的语法。)
expr {…}
好的,这是从数组$B(w2) - 15
的{{1}}元素中读取的值(因为15
)减去$
(数字)。这里的w2
只是一个字符串。其他地方有一个同名的变量是巧合。
就是这样。重新组装,我们看到:
B
将w2
的内容中的减去15的结果分配给变量set w2 [expr {$B(w2) - 15}]
。它确实是所有。 (数组B(w2)
是一个全局数组;请参阅过程顶部的w2
。)
行:
B
这些从全局数组global
获取画布的 half 高度和宽度,删除15个像素的边距,然后设置 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}]
/ {{1}到该值的三分之一,以便绘图的坐标更容易。一旦您意识到画布已将其绘图原点移动(类似于滚动)到其窗口的 center ,它会有所帮助。并且要注意做B
实际上有点顽皮,虽然很可爱;它使用字符串连接来否定一个值,当值为正且没有显式符号时可以,但如果它发生了变化则会很脆弱。与做hbar
相比,它的速度很慢,但是时间更长;计算的成本与命令的长度并不完全匹配。