`array nextelement` -- 当 searchId 无效时无法捕获错误

`array nextelement` -- can't catch error when searchId has been invalidated

我去过 and I'm doing an exercise to implement array foreach (yes, I'm aware of TIP 421).

我正在使用数组 startsearch/anymore/nextelement/donesearch。我现在正试图捕获在搜索过程中添加或删除数组元素时抛出的错误:我发现我无法在我的代码 中捕获该错误,但我可以在交互式会话中捕获它。

这是一个互动环节:

% array set Y {foo bar baz qux}
% set sid [array startsearch Y]
s-1-Y
% set key [array nextelement Y $sid]
foo
% set Y(hello) world
world
% try {
  set key [array nextelement Y $sid]
} trap {TCL LOOKUP} {msg e} {puts "$sid has been invalidated\n$e"}
s-1-Y has been invalidated
-code 1 -level 0 -errorstack {INNER {invokeStk1 ::tcl::array::nextelement Y s-1-Y}} -errorcode {TCL LOOKUP ARRAYSEARCH s-1-Y} -errorinfo {couldn't find search "s-1-Y"
    while executing
"array nextelement Y $sid"
    ("try" body line 1)} -errorline 1
% 

一切顺利。我们可以看到在进行主动搜索时添加数组元素后 searchId 无效:错误代码以 {TCL LOOKUP ARRAYSEARCH}

开头

现在,我将它包装在一个 proc 中,并安排在我启动交互式 tclsh 时获取它(有关详细信息,请参阅 )。这是我的 array foreach 过程的正文:

% info body ::monkeypatches::array_foreach

    if {[llength $vars] != 2} {
        error {array foreach: "vars" must be a 2 element list}
    }
    lassign $vars keyVar valueVar

    # Using the complicated `upvar 1 $arrayName $arrayName` so that any
    # error messages propagate up with the user's array name
    upvar 1 $arrayName $arrayName  $keyVar    key  $valueVar  value

    set sid [array startsearch $arrayName]
    while {[array anymore $arrayName $sid]} {
        # This doesn't seem to be able to catch the error when the user
        # tries to modify the array during a search. Hmm.
        try {
            set key [array nextelement $arrayName $sid]
        } trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
            return -options $e "detected attempt to modify the array while iterating"
        }
        set value [set "${arrayName}($key)"]
        uplevel 1 $body
    }
    array donesearch $arrayName $sid
    return

当我重现捕捉错误的步骤时,我发现我的 try 没有捕捉到错误:

% array set Y {foo bar baz qux}
% array foreach {key val} Y {set Y(hello) world}
couldn't find search "s-1-Y"

用通用的 on error 替换特定的 trap 子句没有帮助。使用 catch 而不是 try.

也不行

我不明白为什么这个过程不能捕捉到错误。有什么想法吗?

如果您查看错误跟踪,您会发现错误并非来自您预期的位置。 (出于本次测试的目的,我将过程创建为 array_foreach。)

% array_foreach {x y} tcl_platform {set tcl_platform(foo) bar;puts $x->$y}
osVersion->16.7.0
couldn't find search "s-1-tcl_platform"
% set errorInfo
couldn't find search "s-1-tcl_platform"
    while executing
"array anymore $arrayName $sid"
    (procedure "array_foreach" line 12)
    invoked from within
"array_foreach {x y} tcl_platform {set tcl_platform(foo) bar;puts $x->$y}"

您在 array nextelement 上设置了陷阱,但没有在 array anymore 上设置陷阱,后者才是真正检测到问题(并抛出)的地方。

我想你最好把循环改成这样:

while true {
    try {
        if {![array anymore $arrayName $sid]} {
            break
        }
        set key [array nextelement $arrayName $sid]
        set value [set ${arrayName}($key)]
    } trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
        return -options $e "detected attempt to modify the array while iterating"
    }
    uplevel 1 $body
}