如何有效地覆盖TCL中的过程局部变量

时间:2016-09-26 14:35:19

标签: tcl

所以我有以下情况:

$ ls -l
-r--r----- 1.tcl
-rw-rw---- 2.tcl

$ cat 1.tcl
proc foo {args} {
  puts "$bar"
}

我需要1.tcl打印"can't read \"bar\""以外的内容。在一个好的编程语言中,显而易见的解决方案是

$ cat > 2.tcl
set -global bar "hello, world"
foo

TCL的合理解决方法是什么?不幸的是,真正的foo是一个很长的函数,我无法在运行时将sed复制到临时文件中。

4 个答案:

答案 0 :(得分:1)

您可以为您的具体示例执行

$ cat 2.tcl
source 1.tcl
set bar "Hello, bar!"
# add a "global bar" command to the foo procedure
proc foo [info args foo] "global bar; [info body foo]"
foo

$ tclsh 2.tcl
Hello, bar!

显然,这并不能很好地扩展。

答案 1 :(得分:1)

如果变量只是未定义,最简单的方法是使用定义修补过程:

proc foo [info args foo] "set bar \"hello, world\" ; [info body foo]"

您还可以使用读取跟踪和帮助程序命令来完成此操作。这消除了我上面提到的问题,其中本地分配破坏了你想要注入的值。

原始过程,添加了一个命令,将局部变量设置为稍后打印的值。

proc foo args {
    set bar foobar
    puts "$bar"
}
% foo
foobar

创建一个全局变量(如果名称相同或无关,则无关紧要。)

set bar "hello, world"

创建一个帮助程序命令,该命令获取局部变量的名称,链接到该变量,并为其分配全局变量的值。由于我们已经知道了名称,我们可以在程序中对其进行硬编码,但这更灵活。

proc readbar {name args} {
    upvar 1 $name var
    global bar
    set var $bar
}

将跟踪添加到foo过程的主体。只要读取局部变量bar,就会触发跟踪,即某些东西试图检索其值。触发跟踪时,将调用命令readbar:它使用全局设置值覆盖变量的当前值。

proc foo [info args foo] "trace add variable bar read readbar; [info body foo]"

% foo
hello, world

如果不希望使用helper命令污染命名空间,可以使用匿名函数:

proc foo [info args foo] [format {trace add variable bar read {apply {{name args} {
    upvar 1 $name var
    global bar
    set var $bar
}}} ; %s} [info body foo]]

文档: applyformatglobalinfoprocputssettraceupvarSyntax of Tcl regular expressions

答案 2 :(得分:0)

source 1.tcl

try {
  foo
} on error {err res} {
  set einfo [dict get $res -errorinfo]
  if { [regexp {no such variable} $einfo] } {
    puts "hello, world"
    return -code 0
  } else {
    puts $einfo
    return -code [dict get $res -code]
  }
}

答案 3 :(得分:0)

默认情况下,Tcl的过程不会将变量解析为除局部变量之外的任何变量。您必须明确要求他们引用其他内容(例如,使用globalvariableupvar)。这意味着总是可以一目了然地看到非本地事情是否正在发生,并且脚本将不起作用。

可以使用变量解析器覆盖此行为,但Tcl并未真正在其脚本接口中公开该API。一些扩展做得更多。例如,可能使用[incr Tcl](即itcl),因为它对其对象中的变量执行此类操作。我不记得Expect是否也这样做了,或者是否使用特殊代码来处理变量。

当然,你可能会偷偷摸摸地改写proc的行为。

rename proc real_proc
real_proc proc {name arguments body} {
    uplevel 1 [list real_proc $name $arguments "global bar;$body"]
}

虽然这很讨厌。