#arrays #tcl #global
#массивы #tcl #глобальный
Вопрос:
У меня есть глобальный массив, который мне нужно иметь возможность переименовать, и, похоже, я не могу сделать это правильно.
array set DataReturn {
red 1
green 5
blue 4
white 9
}
proc _RenameArray {Arr NewArrName} {
global $NewArrName
upvar #0 $Arr $NewArrName
array unset $Arr
}
_RenameArray DataReturn TheArr
Я знаю, что здесь мне многого не хватает. будем признательны за любую помощь!
Комментарии:
1. @DonalF могли бы вы помочь в этом?
Ответ №1:
Это зависит. Переменные Tcl нельзя переименовать, не совсем, но они могут эффективно иметь несколько имен. Вы создаете их с помощью upvar
. В частности, upvar 0
присвоит другое имя переменной в текущей области видимости.
upvar 0 DataReturn TheArr
По сути, это создает TheArr
связанную переменную; ее реальное содержимое является указателем на другую переменную (которая, конечно, может быть массивом), но любое действие над связанной переменной преобразуется в действие над базовой переменной (за исключением изменения ссылки, указывающей на что-то другое). Единственный способ, которым это не является переименованием, заключается в том, что исходная переменная все еще существует и не может быть удалена.
Если вы не можете этого сделать, ваш единственный вариант — скопировать переменную в другую переменную и unset
в исходную. Это тривиально для простых переменных:
set TheNew $TheOld
unset TheOld
и лишь немного сложнее для массивов:
array set TheNew [array get TheOld]
unset TheOld
Однако при этом не сохраняются переменные, которые связываются с массивом, или любые заданные трассировки. Копировать-удалить — это не то же самое, что переименовать.
Преобразование объектов в процедуры немного сложнее, поскольку разрешение имен переменных зависит от контекста, в котором это выполняется. Таким образом, потенциально требуется некоторая осторожность с upvar
и uplevel
. Я не буду обсуждать эти параметры; суть операции в том, о чем я говорил выше, но они выполняются контекстно-зависимым способом ( "#0"
заключен в кавычки из-за подсветки синтаксиса здесь):
proc RenameGlobalVariable {OldName NewName} {
uplevel "#0" [list upvar 0 $OldName $NewName]
}
proc RenameGlobalSimpleVariable {OldName NewName} {
upvar "#0" $OldName old $NewName new
set new $old
unset old
}
proc RenameGlobalArray {OldName NewName} {
upvar "#0" OldName old $NewName new
array set new [array get old]
unset old
}
Комментарии:
1. Конечно, легко скопировать трассировки из одной переменной в другую (по модулю некоторых граничных условий). Чтобы скопировать трассировки, установленные на
foo
вbar
, просто используйтеforeach ti [trace info variable foo] {trace add variable bar {*}$ti}
.2. @Donal Спасибо, я ценю помощь!
Ответ №2:
Это можно упростить:
proc _RenameArray {Arr NewArrName} {
upvar $Arr Temp
global $NewArrName
array set $NewArrName [array get Temp]
uplevel #0 array unset Temp
}
Комментарии:
1. Спасибо! Я ценю обзор кода. Я всегда стремлюсь оптимизировать свой код! Я уверен, что удаление
foreach
немного ускорит процесс.
Ответ №3:
После того, как я некоторое время бился головой о свой стол, я получил это.
proc _RenameArray {Arr NewArrName} {
global [subst $NewArrName];
upvar #0 $Arr Temp;
foreach {Key Value} [array get Temp] {
set [subst $NewArrName]($Key) $Value
}
uplevel #0 array unset $Arr
}
Кто-нибудь знает лучший способ сделать это, я весь внимание!