很抱歉标题无法想象更好。
继承我的问题:
我试图仅在用户点击空间时才更改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而在命名空间中我们使用变量(似乎在一个命令中设置并执行全局)。他们似乎在不同的命名空间中做同样的工作?
答案 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
命令:为什么以及在何处使用它为什么在全局命名空间中我们使用
set
和global
而在namespace
我们使用variable
(似乎set
并且{在一个命令中{1}}。他们似乎在不同的命名空间中做同样的工作?
这是一个很好的问题。实际上,global
的作用与global
非常相似(变量名称加倍),upvar #0
是基本的变量访问命令。它们是您可以随时随地使用的命令。
set
命令比较陌生。它的作用是三重的:
重要的行为实际上是创建当前但未取消的状态,否则你最终会在命名空间中使用variable
(或set
)来转义该命名空间,而是使用全局变量,但并非总是如此。这一切都取决于解析变量的代码的确切行为,这非常棘手。这很难解释得恰到好处,而且非常难以证明。这是造成很多彻头彻尾的错误的原因,绝对不比一个可怕的错误更好。
初始值的设置仅仅是一个棒棒糖;你可以事后直接放array set
而不会产生不良影响。更重要的是,它禁止使用set
从命名空间中提取多个变量,除非您将它们设置为已知值;很适合初始化,但其他用途很糟糕。 (如果您没有猜到,我认为这是Tcl的一个区域,当它被引入时,接口在Tcl 8.0中得到了相当严重的错误。没有一点这很好。)
关键外卖是这样的:
variable
来声明变量,因为这是确保语义可预测的唯一方法。然后,您可以以任何方式初始化它们。 (如果你正在创建数组,你必须这样做。)完全限定的变量名称没有与之相关的疯狂。在这种情况下,Tcl总是知道如何查找你正在命名的东西。