使用bind操作proc中的变量

时间:2013-10-19 17:09:50

标签: tcl tk

很抱歉标题无法想象更好。

继承我的问题:

我试图仅在用户点击空间时才更改proc中的变量。如果用户希望更多地命中空间,那么proc会自动循环,一旦变量将增加。

继承人我所知道的:

有很多方法可以解决这个问题。您可以在proc中传递变量,您可以使用global或upvar链接变量和/或如果您在命名空间中,则可以使用变量。但唯一似乎与我合作的是全球性的。我感觉这是因为全球化了一个链接,但如果那是真的那么变量也应该起作用,对吧?

这是我的测试代码:

proc test1 {} {
    global testing
    bind . <Key-a> {incr testing}
    puts $testing
    puts "test2"
    after 100 test2
}


namespace eval test2 {
    variable testing 0
    namespace export {[a-z]*}
    proc of1 {} {
            variable testing
            bind . <Key-a> {incr testing}
            puts $testing
            after 100 test3::of1
    }
}


proc test3 {testing} {
     bind . <Key-a> {incr testing}
     puts $testing
     puts "test4"
     after 100 test4 $testing
 } 
set testing 0
#test1 
test2::of1
#test3 0
grid .c 

问题:

为什么在全局命名空间中我们使用set和global而在命名空间中我们使用变量(似乎在一个命令中设置并执行全局)。他们似乎在不同的命名空间中做同样的工作?

1 个答案:

答案 0 :(得分:1)

回调中的变量

bind命令注册的脚本 - 也包括after事件和fileevent回调 - 在全局范围内进行评估,因为它们可能在定义的过程后很长时间被调用他们回来; Tcl不进行范围捕获(这实际上是一个非常复杂的功能,因此除非有人编写大量代码,否则它不太可能很快到来)。这意味着您希望程序注意到更改的变量必须具有全局范围。

但是,为了讨论的目的,命名空间变量可以作为全局变量计算,因为它们可以从全局上下文中命名(实际的局部变量不是)。这意味着我们可以通过多种方式构建一个脚本,该脚本从bind定义的回调中访问命名空间变量。这是一个更好的:

bind . <Key-a> [namespace code {incr testing}]

这实际上与此相同:

bind . <Key-a> [list namespace eval [namespace current] {incr testing}]

(在这个例子中有一些严格的差异无关紧要。)

进行回调的另一种方法是:

bind . <Key-a> [list incr [namespace which -variable testing]]

在这种情况下会更像:

bind . <Key-a> [list incr [namespace current]::testing]

如果事情变得比这个玩具示例更复杂,那么是时候停止在绑定脚本中直接更新变量,而是编写一个帮助程序。这总是简化了很多事情。或者使用类/对象来封装细节。


variable命令:为什么以及在何处使用它

  

为什么在全局命名空间中我们使用setglobal而在namespace我们使用variable(似乎set并且{在一个命令中{1}}。他们似乎在不同的命名空间中做同样的工作?

这是一个很好的问题。实际上,global的作用与global非常相似(变量名称加倍),upvar #0是基本的变量访问命令。它们是您可以随时随地使用的命令。

set命令比较陌生。它的作用是三重的:

  1. 如果在命名空间上下文中调用并且该命名空间中的变量不存在,则会在当前但未设置状态下创建该变量。
  2. 如果在具有局部变量的上下文中调用,它会将局部变量与名称(在将所有内容剥离到最后一个名称空间分隔符之后)链接到具有名称的名称空间变量(如果有限定符,则使用整个提供的名称,并且解析与当前上下文命名空间相关的非绝对名称。此强制命名空间变量以当前但未设置状态存在。
  3. 如果给出了值,则将namespace变量设置为该值。这摆脱了现在但未解决的问题。
  4. 重要的行为实际上是创建当前但未取消的状态,否则你最终会在命名空间中使用variable(或set)来转义该命名空间,而是使用全局变量,但并非总是如此。这一切都取决于解析变量的代码的确切行为,这非常棘手。这很难解释得恰到好处,而且非常难以证明。这是造成很多彻头彻尾的错误的原因,绝对不比一个可怕的错误更好。

    初始值的设置仅仅是一个棒棒糖;你可以事后直接放array set而不会产生不良影响。更重要的是,它禁止使用set从命名空间中提取多个变量,除非您将它们设置为已知值;很适合初始化,但其他用途很糟糕。 (如果您没有猜到,我认为这是Tcl的一个区域,当它被引入时,接口在Tcl 8.0中得到了相当严重的错误。没有一点这很好。)

    关键外卖是这样的:

    • 始终在命名空间中使用variable来声明变量,因为这是确保语义可预测的唯一方法。然后,您可以以任何方式初始化它们。 (如果你正在创建数组,你必须这样做。)

    完全限定的变量名称没有与之相关的疯狂。在这种情况下,Tcl总是知道如何查找你正在命名的东西。