我有一个小问题,我现在无法解释。我创建了一个简约的代码片段来显示我的问题或者不了解tcl命名空间是如何工作的。
所以我有文件test.tcl:
namespace eval test {
proc print {file_name} {
namespace inscope ::test2 {
printFileName $::test::file_name
}
}
}
namespace eval test2 {
proc printFileName {file_name} {
puts $file_name
}
}
比我使用tclsh并运行:
source test.tcl
test::print test.dat
返回:
can't read "::test::file_name": no such variable
为什么test :: print的参数不应该在:: test namescope中? 在命名空间inscope {} 之前,我有一个简单的 set :: test :: filename $ filename 解决方法。
但我不满意,因为我在这里错过了一些东西。
我不能只运行 :: test2 :: printFileName $ file_name ,因为我的真实世界代码更复杂,并且不会只运行一个命令,它会提供一个命令列表,这些命令都在不同的命名空间中
答案 0 :(得分:2)
局部变量不是命名空间变量。特别是,即使使用变量链接(upvar
和global
以及variable
等基础的机制),形式参数变量也是 never 命名空间变量。这就是变量的映射方式,因为它的执行速度非常快;程序入口代码是Tcl实现代码中最热门的部分之一,因此我们尽最大努力使其尽可能快。
但一切都没有丢失!
您可以轻松地将值复制到命名空间变量,甚至可以使用跟踪和upvar
进行操作,以使局部变量可以从该内部范围写回。 (你可能不得不使用info level
来搜索堆栈以注入写入的位置,这将是可怕的混乱,但它会起作用。)
但我要做的是使命令提供值(并且可能允许在必要时写回)。我认为它有点清洁。
namespace eval test {
proc print {file_name} {
proc ::test2::file_name {} [list return $file_name]
namespace eval ::test2 {
printFileName [file_name]
}
}
}
namespace eval test2 {
proc printFileName {file_name} {
puts $file_name
}
}
通过使用namespace path
,您可以更加优雅,这样您每次打电话时都不必构建全新的程序。 interp alias
也可以提供帮助(如果您熟悉函数式编程中的那类内容,那么这是一个很好的方式来进行参数化。)
答案 1 :(得分:1)
此处的问题是,::test::file_name
引用file_name
名称空间中的::test
变量,不引用本地变量file_name
你要的那个。这就是变量名称解析的工作方式。作为命令参数的局部变量不驻留在封闭的命名空间中。
此外,在脚本中直接调用namespace inscope
是不常见的。我会这样写:
namespace eval test {
proc print {file_name} {
{*}[namespace eval ::test2 namespace code printFileName] $file_name
}
}
namespace eval test2 {
proc printFileName {file_name} {
puts $file_name
}
}
如果您的Tcl版本中没有{*},可能会有一些变化。
但你最后一条关于无法直接运行命令的评论让我怀疑这种方法可能还没有解决你的问题。