Уроки Tcl

Начало Уроки Tk Файлы
Статьи Уроки Tcl Гостевая книга
Предыдущий урок - #12: Варианты аргументов процедур и возвращаемых значений

#13: Область видимости переменных - global и upvar

Tcl обрабатывает имена переменных раздельно в двух областях: в локальной области процедуры и глобальной области (код и переменные вне процедур).

Локальная область видимости означает, что вы можете иметь переменные в процедуре с таким же именем, как и в основной программе, при этом значения этих переменных будут различаться, по сути это будут разые переменные. Например:

proc ShortLoop {} {
	for{set i 0} {$i < 10} {incr i} { ... вычисления ..}
}
...
for {set i 0} {$i <  11} {incr i} { ShortLoop }

Эта програма никогда бы не завершилась, если переменная i в процедуре ShortLoop была бы та же, что и в основной программе. Каждый раз при вызове ShortLoop переменная i устанавливалась бы в 10.

Иногда требуется получить доступ к переменной в другой области видимости. Например, если процедура должна изменить значение аргумента. Бывает удобнее иметь одну переменную в программе, чем делать локальные копии в каждой процедуре.

Область видимости переменной может быть изменена командой global или upvar.

Команда global делает переменную в локальной области (например, внутри процедуры) видимой в глобальной области. В примере выше если бы в начале процедуры была команда

global i

То переменная внутри процедуры стала бы глобальной и цикл стал бы бесконечным.

Команда upvar действует аналогично. Upvar связывает имя переменной в текущей области видимости с переменной в другой области. Обычно используется для передачи по ссылке в процедуру.

Синтаксис upvar следующий:

upvar ?уровень? внешПерем1 мояПерем1 ?внешПерем2? ?мояПерем2? ... ?

Upvar делает мояПерем1 ссылкой на внешПерем1, и мояПерем2 становится ссылкой на внешПерем2, и так далее. Переменная внешПерем должна быть обьявлена определенном уровне относительно текущей процедуры. По умолчанию уровень равен 1, это следующий уровень вверх. Если уровень равен 0, то это сслыка на переменную глобального уровня. В этом случае процедуре не нужно знать, на каком уровне вложенности находится процедура, и на сколько уровней нужно подняться.

Ещё несколько слов о стиле программирования.

По-моему, использовать upvar с чем-нибудь, кроме 0 или 1 - напрашиваться на проблемы. Процедуры с upvar 2 ... надо хорошо знать код, чтобы связывать переменные так далеко друг от друга. Когда надо будет использовать процедуру в другой программе придётся исправлять код на два уровня вверх. Даже небольшие изменения, например изменение имени переменной, могут быть причиной потери работоспособности кода.

Без изспользования avoid сложно обойтись, но вы должны иметь как можно меньше глобальных переменных. Если в начале требуется много глобальных переменных, то возможно надо заново продумать структуру программы. Рекомендуется добавлять к глобальным переменным определённую приставку, чтобы отделить их от похожих глобальных переменных, которые могут быть в других программах, которые вы возможно захотите подключить позже. Например, для переменной itemCount используйте приставку TestApp_itemCount. Это поможет избежать неожиданных ошибок с именами.

Пример:

# Пример использования upvar
# Преобразование значения в положительное число перед использованием
proc SetPositive {variable value } {
	upvar $variable myvar;
	if {$value < 0} { set myvar [expr -$value];} else {set myvar $value;}
	return $myvar;
}

SetPositive x 5;
SetPositive y -5;

puts "X : $x    Y: $y\n"
# размещение Upvars

# Процедура второго уровня - будет вызвана процедурой one
proc two {y} {
  upvar 1 $y z	;# связь вызываемого значения с переменной z
  upvar 2 x a	;# связь переменной x с переменной a
  puts "процедура twо: Z: $z A: $a"	;# вывод значений, просто для проверки
  set z 1 ;# установка z, передача на уровень выше числа 1
  set a 2 ;# установка x, передача числа 2 на два уровня выше
  }

;# Процедура первого уровня - будет вызвана из основной программы
proc one {y} {
	upvar $y z			;# это связь вызываемого значения с переменной z
	puts "процедура one: Z: $z"		;# Вывод значения, должно быть 5
	two z			;# вызов процедуры two, которая изменит значение
}

one y	;# вызов one, и вывод X и Y после вызова.
puts "\nX: $x  Y: $y"

Следующий урок - #14: Cписковые структуры данных - list
Горбачев "Yurez" Юрий  
Хостинг от uCoz