在 TCL 上使用另一个 proc 中的 proc

Use proc from inside another proc on TCL

我正在尝试在 tcl 中编写一个脚本来对 VMD 执行一些分析。首先,我在一个过程中创建一个原子选择,然后我试图在另一个过程中使用它。

VMD 中的原子选择是作为 Tcl 函数实现的。从 atomselect 返回的数据是要使用的函数的名称。您可以阅读更多 here.

当我在 proc 外部创建原子选择时,我可以在一个内部使用它,只需将它作为 global 传递即可。但是现在我在一个内部创建它,当我尝试使用它时它只是一个带有过程名称的字符串。

正在恢复,

这个有效:

set all [atomselect top all]

proc test {} {
    global all
    $all num
}

test # Outuputs 7111

但这不是:

proc create {} {
    global all
    set all [atomselect top all]
}

proc test {} {
    global all
    $all num
}

create
test # Outputs -> invalid command name "atomselect1"

问题是 atomselect 命令创建的命令在过程 returns 时被删除。我强烈怀疑这是使用局部变量删除跟踪(在一些未使用的变量上)完成的。当 atomselect 在全局范围内是 运行 时,没有这样的自然删除事件(除非全局命名空间被删除,这是一个解释器结束事件)所以跟踪永远不会被调用并且创建的命令仍然存在无限期地。

如果我们像这样重写您的创建:

proc create {} {
    global all
    # Double quotes just because of the highlighting here
    set all [uplevel "#0" atomselect top all]
}

然后 atomselect 运行 在全局范围内(并且创建的命令应该保留)但是所有其余代码都在您的过程中。

更恰当的做法应该是这样:

proc create {} {
    global all
    # Double quotes just because of the highlighting here
    set all [uplevel "#0" [list atomselect top all]]
    # Or this, to better show that we're talking about running a command:
    #   set all [uplevel "#0" [list \
    #       atomselect top all]]
}

当您传递带有空格(或其他元语法字符)的参数时,这一点变得更加重要,因为 list 不仅会生成列表,还会生成无替换命令。 (事实上​​ ,构建命令和命令片段绝对是 list 命令的主要用途之一;普通数据存储和操作的实际列表往往是用其他命令制作的。)