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"
}

过程中的局部变量不需要声明,但如果你想访问一个非局部变量,你必须使用命令(通常globalupvar) 将其纳入范围。

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

好的,这是从数组 Bw2 元素读取的值(由于 $)减去 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}] 相比,虽然更长,但速度较慢;计算成本与命令的长度不完全匹配。