:TITLE: Escopo de variáveis - global e upvar
;#
;# RCSID: $Header: /home/clif/CVS_ROOT/TclTutor/Tcl13.lsn,v 1.9 2000/04/24 16:17:50 clif Exp $
;# Copyright (c) 1995 Clif Flynt
;# 9300 Fleming Rd.
;# Dexter, MI  48130
;# clif@cflynt.com
;# Veja o arquivo "AVISO" com os termos da licença.
;#
;#*********************************************************
;# Portuguese translation: Lucio Agostinho Rocha
;# E-mail: outrosdiasvirao@yahoo.com.br
;# Last update: 03/01/2006
;#*********************************************************
:LESSON_TEXT_START_LEVEL 0:
Tcl avalia o nome de uma variável em um de dois escopos: o escopo
local dentro de um proc, e um escopo global (o código e variáveis
fora de qualquer proc). Como C, Tcl define apenas um espaço global.
<P>
O escopo no qual uma variável será avaliada pode ser alterado
com o comando <CODE>global</CODE> ou <CODE>upvar</CODE>.
<P>
O comando <CODE>global</CODE> irá fazer com que uma variável
em um escopo local seja avaliada no escopo global.
<P>
O comando <CODE>upvar</CODE> se comporta de forma similar no
que diz respeito à mudança de escopo da variável, mas diferentemente
na forma como ele pode mapear a variável para um novo nome.
<CODE>Upvar</CODE> vincula o nome de uma variável no escopo
atual a uma variável em um escopo diferente. Isso é comumente
usado para simular a <I>passagem por referência</I> em procs.
<P>
Por exemplo, este código irá criar um <I>link</I> entre a variável
<CODE>y</CODE> no proc e a variável <CODE>z</CODE> no escopo global:
<CODE><PRE>
proc p {x} {
  upvar $x y
  puts $y
  }
set z A
p z
</PRE></CODE>
A sintaxe para <CODE>upvar</CODE> é:
<P><B>upvar</B> <I> ?nível? outraVar1 minhaVar1 ?outraVar2 minhaVar2 ...? </I>
<P>
<CODE>Upvar</CODE> faz com que <I>minhaVar1</I> se torne uma referência a
<I>outraVar1</I>, e <I>minhaVar2</I> se torne uma referência a
<I>outraVar2</I>, etc. A variável <I>outraVar</I> é declarada para estar
no <I>nível</I> relativo ao procedimento atual. Por padrão 
<I>nível</I> é 1, o próximo nível acima. Se <I>nível</I> for 0, 
então a referência é para uma variável no nível global.
<P>
Note que como há apenas um espaço global é surpreendentemente fácil
ter conflitos de nome se você estiver importando código de outras pessoas
e não for cuidadoso. É recomendado que você inicie variáveis globais com
um prefixo identificável para ajudar a evitar conflitos não esperados.
:TEXT_END:

:LESSON_TEXT_START_LEVEL 1:
Tcl avalia o nome de uma variável em um de dois escopos: o escopo
local dentro de um proc, e um escopo global (o código e variáveis
fora de qualquer proc). Como C, Tcl define apenas um espaço global.
<P>
O escopo no qual uma variável será avaliada pode ser alterado
com o comando <CODE>global</CODE> ou <CODE>upvar</CODE>.
<P>
O comando <CODE>global</CODE> irá fazer com que uma variável
em um escopo local seja avaliada no escopo global.
<P>
O comando <CODE>upvar</CODE> se comporta de forma similar no
que diz respeito à mudança de escopo da variável, mas diferentemente
na forma como ele pode mapear a variável para um novo nome.
<CODE>Upvar</CODE> vincula o nome de uma variável no escopo
atual a uma variável em um escopo diferente. Isso é comumente
usado para simular a <I>passagem por referência</I> em procs.
<P>
A sintaxe para <CODE>upvar</CODE> é:
<P><B>upvar</B> <I> ?nível? outraVar1 minhaVar1 ?outraVar2 minhaVar2 ...? </I>
<P>
<CODE>Upvar</CODE> faz com que <I>minhaVar1</I> se torne uma referência a
<I>outraVar1</I>, e <I>minhaVar2</I> se torne uma referência a
<I>outraVar2</I>, etc. A variável <I>outraVar</I> é declarada para estar
no <I>nível</I> relativo ao procedimento atual. Por padrão 
<I>nível</I> é 1, o próximo nível acima. Se <I>nível</I> for 0, 
então a referência é para uma variável no nível global.
<P>
A opinião pessoal do autor deste tutorial (Clif Flynt) é que usar
upvar com qualquer outra coisa, com exceção de 0 ou 1, é buscar
problemas.
<P>
O uso de <I>global</I> é difícil de evitar, mas você deveria evitar
ter muitas variáveis globais. Se você começa a precisar de muitas
variáveis globais, seria bom que você revisasse o design do seu programa.
<P>
Note que como há apenas um espaço global é surpreendentemente fácil
ter conflitos de nome se você estiver importando código de outras pessoas
e não for cuidadoso. É recomendado que você inicie variáveis globais com
um prefixo identificável para ajudar a evitar conflitos não esperados.
:TEXT_END:

:LESSON_TEXT_START_LEVEL 2:
Tcl avalia o nome de uma variável em um de dois escopos: o escopo local
dentro de um proc, e um escopo global (o código e variável fora de
qualquer proc).
<P>
O escopo local significa que você pode ter variáveis em seu proc com
os mesmos nomes de variáveis na chamada do proc (ou linha de código
principal) sem que os valores de uma variável entrem em conflito com
os valores de outra. Por exemplo:
<CODE><PRE>
proc pequenoLaco {} {
  for{set i 0} {$i &lt; 10} {incr i} { ... faz alguma coisa ..}
  }

...
for {set i 0} {$i &lt;  11} {incr i} { pequenoLaco }
</PRE></CODE>
Esse código nunca terminaria se o <B>i</B> em <B>pequenoLaco</B> fosse o
mesmo <B>i</B> da linha de código principal.
Cada vez que <B>pequenoLaco</B> fosse chamado, ele retornaria o valor de
<B>i</B> para 10.
<P>
Algumas vezes, no entanto, você precisa acessar uma variável em um
escopo diferente do atual. Por exemplo, quando você tem um proc que
precisa mudar o valor de um argumento, você precisa dessa facilidade.
É também muito mais conveniente manter uma cópia do estado da variável
que todo o código pode referenciar do que manter passagem de cópias 
daquela variável em todo o programa.
<P>
O escopo no qual uma variável será avaliada pode ser alterado com o
comando <CODE>global</CODE> ou <CODE>upvar</CODE>.
<P>
O comando <CODE>global</CODE> irá fazer com que uma variável em um escopo 
local seja avaliada em um escopo global. No exemplo anterior, se houvesse
uma linha <CODE>global i</CODE> no <I>pequenoLaco</I> antes da repetição
do <I>for</I>, então <B>i</B> seria global e o laço nunca terminaria.
<P>
O comando <CODE>upvar</CODE> se comporta de forma similar. <CODE>Upvar</CODE> 
vincula o nome de uma variável no escopo atual a uma variável em um escopo
diferente. Isso é comumente usado para simular <I>passagem por referência</I>
em procs.
<P>
A sintaxe para <CODE>upvar</CODE> é:
<P><B>upvar</B> <i> ?nível? outraVar1 minhaVar1 ?outraVar2 minhaVar2 ...? </i>
<P>
<CODE>Upvar</CODE> faz com que <I>minhaVar1</I> se torne uma referência
a <I>outraVar1</I>, e <I>minhaVar2</I> se torne uma referência a 
<I>outraVar2</I>, etc. A variável <I>outraVar</I> é declarada para estar
no <I>nível</I> relativo ao procedimento atual. Por padrão, <I>nível</I>
é 1, o próximo nível acima. Se <I>nível</I> for 0, então a referência é para
uma variável no escopo global. Neste caso, um proc não precisa saber quantos
níveis abaixo ele foi chamado para referenciar uma variável a muitos níveis
acima.
<P>
Outra dica no estilo de programação
<P>
A opinião pessoal do autor deste tutorial (Clif Flynt) é que usar
upvar com qualquer outra coisa, exceto 0 ou 1, é buscar problemas.
Um proc com <CODE>upvar 2 ...</CODE> nele tem também muito 
conhecimento de código intrincado que está também longe do proc, e você
será pego algum dia quando tentar usar aquele procedimento em outro
código, ou precisar modificar o código dois níveis acima. Mesmo
alguma coisa simples, como mudar um nome de variável, poderia
fazer com que o programa parasse de funcionar.
<P>
O uso de <I>global</I> é difícil de evitar, mas você deveria evitar
ter muitas variáveis globais. Se você começa a precisar de muitas
variáveis globais, seria bom que você revisasse o design do seu programa.
É recomendado que você inicie todos os globais em um pacote com
um prefixo comum para separá-los de globais similares que possam
existir em outros pacotes que você irá incorporar mais tarde.
Ao invés de um nome de variável como <CODE>itemCont</CODE> 
use <CODE>TesteAp_itemCont</CODE>. Isso irá ajudar a evitar
conflitos de nomes não esperados.
:TEXT_END:

:CODE_START:
;# Um exemplo de upvar
;# Converte um valor para um número positivo antes da atribuição.
proc atribuirPositivo {variavel valor} {
  upvar $variavel minhaVar
  if {$valor < 0} { set minhaVar [expr -$valor] } else { set minhaVar $valor }
  return $minhaVar;
  }

atribuirPositivo x 5;
atribuirPositivo y -5;

puts "X : $x    Y: $y\n"

;# upvars aninhados

;# Um proc de segundo nível - Este será chamado pelo proc 'um'
proc dois {y} {
  upvar 1 $y z			;# Vincula o valor chamado à variável z
  upvar 2 x a			;# Vincula a variável x dois níveis acima para a
  puts "dois: Z: $z A: $a"	;# Saída dos valores, apenas para confirmar
  set z 1			;# Atribui z, a variável passada para 1
  set a 2			;# Atribui x, dois níveis acima para 2
  }

;# Um proc de primeiro nível - Este será chamado pelo código do espaço global
proc um {y} {
  upvar $y z			;# Este vincula o valor chamado à variável z
  puts "um: Z: $z"		;# Saída daquele valor, para conferir que é 5
  dois z			;# Chama o proc dois, que irá mudar o valor
  }

um y				;# Chama um, e as saídas de X e Y depois da chamada.
puts "\nX: $x  Y: $y"
:TEXT_END:

