TK/TCL,无法理解这个井字游戏代码片段
TK/TCL, trouble understanding this tic tac toe code snippet
谁能帮我解释一下下面的代码片段的作用。此代码片段来自 http://wiki.tcl.tk/12374。它旨在创建井字游戏。没有多少资源可以用来理解 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
怎么定义为引用自身???作者使用这些变量来绘制井字线,但我什至不知道这些变量是什么意思。这些变量是否指定了 canvas 的某个维度,以便作者可以使用它来将特定区域绑定到点击活动?
如果我理解了这四个变量,其他一切都会有意义!
这是板子的图片:
Tcl 中的变量(Tk 只是一个 window 位于 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
或 upvar
) 将其纳入范围。
proc variableExample {formalArgument1 formalArgument2} {
set localVar1 "abc"
set localVar2 "def"
global thisOtherVar
append thisOtherVar "ghi" $formalArgument1
puts "Currently, got $localVar1 $localVar2 and '$thisOtherVar'"
}
将global
放在程序的顶部是很传统的做法,但完全没有必要。它的效果从您执行它的地方一直持续到过程调用结束。 Tcl 的语义严格 可操作,具有极其严格定义的评估顺序(它是从左到右,总是)。
现在,数组是集合 变量。数组的每个元素本身就是一个变量。它们不同于普通的简单变量,尽管整个数组的名称与简单变量采用相同的命名方案。你不能在同一个范围内有一个简单的 foo
和一个 foo(bar)
(如果没有先 unset
一个,它会删除变量)。元素中的键是 strings — 幕后的实现是高性能哈希 table — 完全不是变量,这意味着 B(w2)
和 w2
变量完全不同;它们根本不是一回事。但是,我们可以使用变量(和其他 Tcl 替换)来计算要用作键的字符串,因此我们可以这样做:
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}
分解成碎片:
expr {…}
这会产生对大括号中的表达式求值的结果。 强烈 建议您将所有表达式都用大括号括起来;它更安全,更快。什么表情? (请注意,表达式使用与 Tcl 其余部分不同的语法。)
$B(w2) - 15
好的,这是从数组 B
的 w2
元素读取的值(由于 $
)减去 15
(数字)。这里的w2
是只是一个字符串。别处有个同名变量纯属巧合
就是这样。重新组装零件,我们看到:
set w2 [expr {$B(w2) - 15}]
将B(w2)
的内容减15的结果赋给变量w2
。这是 all 它所做的。 (数组 B
是全局数组;请参阅过程顶部的 global
。)
行数:
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}]
这些从全局数组B
中获取canvas的half高度和宽度,移除15个像素作为边距,然后设置hbar
/vbar
到该值的三分之一,以便绘制坐标更容易。一旦您意识到 canvas 已将其绘图原点移动(类似于滚动)到其 window 的 center,它就会有所帮助。请注意,-$hbar
虽然可爱,但实际上有点调皮;它使用字符串连接来否定一个值,当值是正数并且没有明确的符号时这是可以的,但是如果它发生变化就会很脆弱。与 [expr {-$hbar}]
相比,虽然更长,但速度较慢;计算成本与命令的长度不完全匹配。
谁能帮我解释一下下面的代码片段的作用。此代码片段来自 http://wiki.tcl.tk/12374。它旨在创建井字游戏。没有多少资源可以用来理解 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
怎么定义为引用自身???作者使用这些变量来绘制井字线,但我什至不知道这些变量是什么意思。这些变量是否指定了 canvas 的某个维度,以便作者可以使用它来将特定区域绑定到点击活动?
如果我理解了这四个变量,其他一切都会有意义!
这是板子的图片:
Tcl 中的变量(Tk 只是一个 window 位于 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
或 upvar
) 将其纳入范围。
proc variableExample {formalArgument1 formalArgument2} {
set localVar1 "abc"
set localVar2 "def"
global thisOtherVar
append thisOtherVar "ghi" $formalArgument1
puts "Currently, got $localVar1 $localVar2 and '$thisOtherVar'"
}
将global
放在程序的顶部是很传统的做法,但完全没有必要。它的效果从您执行它的地方一直持续到过程调用结束。 Tcl 的语义严格 可操作,具有极其严格定义的评估顺序(它是从左到右,总是)。
现在,数组是集合 变量。数组的每个元素本身就是一个变量。它们不同于普通的简单变量,尽管整个数组的名称与简单变量采用相同的命名方案。你不能在同一个范围内有一个简单的 foo
和一个 foo(bar)
(如果没有先 unset
一个,它会删除变量)。元素中的键是 strings — 幕后的实现是高性能哈希 table — 完全不是变量,这意味着 B(w2)
和 w2
变量完全不同;它们根本不是一回事。但是,我们可以使用变量(和其他 Tcl 替换)来计算要用作键的字符串,因此我们可以这样做:
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}
分解成碎片:
expr {…}
这会产生对大括号中的表达式求值的结果。 强烈 建议您将所有表达式都用大括号括起来;它更安全,更快。什么表情? (请注意,表达式使用与 Tcl 其余部分不同的语法。)
$B(w2) - 15
好的,这是从数组 B
的 w2
元素读取的值(由于 $
)减去 15
(数字)。这里的w2
是只是一个字符串。别处有个同名变量纯属巧合
就是这样。重新组装零件,我们看到:
set w2 [expr {$B(w2) - 15}]
将B(w2)
的内容减15的结果赋给变量w2
。这是 all 它所做的。 (数组 B
是全局数组;请参阅过程顶部的 global
。)
行数:
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}]
这些从全局数组B
中获取canvas的half高度和宽度,移除15个像素作为边距,然后设置hbar
/vbar
到该值的三分之一,以便绘制坐标更容易。一旦您意识到 canvas 已将其绘图原点移动(类似于滚动)到其 window 的 center,它就会有所帮助。请注意,-$hbar
虽然可爱,但实际上有点调皮;它使用字符串连接来否定一个值,当值是正数并且没有明确的符号时这是可以的,但是如果它发生变化就会很脆弱。与 [expr {-$hbar}]
相比,虽然更长,但速度较慢;计算成本与命令的长度不完全匹配。