2012-06-15 15 views
8

Tengo una pregunta sobre el comando upvar en TCL. Usando el comando upvar, tenemos una referencia a una variable global o una variable local en otro procedimiento. Vi el siguiente código:cómo funciona el comando upvar en TCL?

proc tamp {name1 name2} { 
    upvar $name1 Ronalod 
    upvar $name2 Dom 
    set $Dom "Dom" 
} 

este procedimiento se llama como tamp name1 name2, y no hay variables de nombre1 mundial, nombre2 definida fuera de ella, cómo funciona este upvar en este caso?

Respuesta

11

Cuando se llama a upvar 1 $foo bar, se encuentra la variable en el alcance de la persona que llama cuyo nombre está en la variable foo, y hace que el bar variable local en un alias para ella. Si la variable no existe, es creado en un estado no configurado (es decir, el registro de variable existe pero no tiene ningún valor. De hecho, la implementación usa un NULL para representar esa información, por lo que Tcl no tiene NULL equivalente; NULL indica inexistencia) pero el enlace aún se crea. (Sólo se deja penetrar hacia abajo cuando el ámbito local es destruida o si upvar se utiliza para señalar la variable local en otra cosa.)

Así que vamos a ver lo que su código es realmente haciendo:

proc tamp {name1 name2} { 
    upvar $name1 Ronalod 
    upvar $name2 Dom 
    set $Dom "Dom" 
} 

La primera línea dice que estamos creando un comando llamado tamp como un procedimiento, que ese procedimiento tendrá dos argumentos formales obligatorios, y que esos argumentos se llaman name1 y name2.

La segunda línea dice que estamos unir un nombre de variable en la persona que llama (el indicador de nivel de 1 mi explicación anterior es opcional, pero muy recomendable en el código idiomática) que viene dado por la variable name1 (es decir, la primera argumento para el procedimiento) a la variable local Ronalod. De ahora en adelante, todos los accesos a esa variable local (hasta el final de la vida del marco de la pila) se realizarán realmente en la variable enlazada en la persona que llama.

La tercera línea es prácticamente la misma, excepto con name2 (segundo argumento) y Dom (variable local).

La cuarta línea es realmente bastante funky. Lee la variable Dom para obtener un nombre de variable (es decir,, la variable nombrada en el segundo argumento de la llamada a procedimiento) y establece esa variable con nombre al valor Dom. Recuerde, en Tcl usa $ a lea desde una variable, no a hable de la variable.

El resultado de la llamada a procedimiento será el resultado del último comando en su cuerpo (es decir, el literal Dom dado que set produce el contenido de la variable como resultado, un valor que acaba de asignar). (La última línea no es interesante ya que es solo el final del cuerpo del procedimiento.)

El resultado neto de llamar a este comando en realidad va a ser prácticamente nulo a menos que el segundo argumento nombre una variable que contenga Ronalod o Dom. Eso es bastante confuso. Por supuesto, el bit confuso es realmente ese funky set con un primer argumento variable. (Eso es casi siempre una mala idea, es un indicador de mal olor Código.) ¿Había utilizado esta vez, las cosas habrían sido más sencillo:

set Dom "Dom" 

En este caso, la variable que Dom está acoplada a (es decir, el designado por el segundo argumento para el procedimiento) se establecería en Dom; las variables estarían siendo pasadas por referencia. ¡Ese extra $ hace una gran diferencia!

1

name1 y name2 no existen en el alcance de la llamada: son simplemente parámetros para su proceso. Por ejemplo, puede llamar al proc de la siguiente manera:

% set first "James" 
James 
% set last "Bond" 
Bond 
% tamp first last 
Dom 

Tal y como está el proc realmente no hacer nada. Si modifica la última línea de la siguiente manera, a continuación, tiene más sentido:

proc tamp {name1 name2} { 
    upvar $name1 Ronalod 
    upvar $name2 Dom 
    set Dom "Dom" 
} 
% tamp first last 
Dom 
% puts $first 
James 
% puts $last 
Dom 

Una de las mejores guías que he visto hasta el uso de upvar y de nivel superior es la guía de Rob Mayoff http://www.dqd.com/~mayoff/notes/tcl/upvar.html


I Estoy agregando un ejemplo más para ayudarlo a ver que name1 y name2 son solo parámetros de entrada y no necesitan existir globalmente. Ejecute este ejemplo en tclsh y vea si tiene más sentido.

% proc tamp {name1 name2} { 
    upvar $name1 Ronalod 
    upvar $name2 Dom 
    puts "Ronalod=$Ronalod, Dom=$Dom" 
    set Ronalod "Brian" 
    set Dom "Fenton" 
    puts "NOW: Ronalod=$Ronalod, Dom=$Dom" 
} 
% 
% tamp name1 name2 
can't read "Ronalod": no such variable 
% set first "James" 
James 
% set last "Bond" 
Bond 
% tamp first last 
Ronalod=James, Dom=Bond 
NOW: Ronalod=Brian, Dom=Fenton 
% puts $first 
Brian 
% puts $last 
Fenton 
+0

si name1 y name2 son simplemente parámetros, luego en el proceso, ¿qué significa upvar? el mismo significado que "set name1 Ronaldo"? –

+0

¡Hola! "upvar $ name1 Ronalod" significa literalmente crear una variable de ámbito local llamada Ronalod, que está vinculada a la variable referenciada por $ name1. Entonces, cualquier cambio en Ronalod también cambiará la variable referenciada por $ name1. Puedes ver que en mi ejemplo, el valor de "último" fue cambiado por establecer Dom "Dom". – TrojanName

+0

gracias, pero en mi código, no hay variables globales name1, name2, así que si configura Dom "Dom", significa que la variable Dom se cambia, y la variable a la que hace referencia $ nombre1 también se cambia, pero en mi caso, la variable a la que hace referencia $ nombre1 no existe? –

1

Cuando el intérprete Tcl introduce un procedimiento escrito en Tcl, crea una tabla especial de variables locales para ese procedimiento mientras se está ejecutando su código. Esta tabla puede contener tanto variables locales "reales" como "enlaces" especiales a otras variables. Dichos enlaces son indistinguibles de las variables "reales" siempre que se trate de comandos Tcl (como set, unset, etc.).

Estos enlaces son creados por el comando upvar, y es capaz de crear un enlace a cualquier variable en cualquier marco de pila (incluido el marco — alcance global 0).

Desde Tcl es muy dinámico, sus variables pueden ir y venir en cualquier momento y por lo tanto una variable vinculada al por upvar podrían no existir en el momento que se crea un enlace a ella, observar:

% unset foo 
can't unset "foo": no such variable 
% proc test name { upvar 1 $name v; set v bar } 
% test foo 
bar 
% set foo 
bar 

Tenga en cuenta que Primero demuestro que la variable llamada "foo" no existe, luego la configuro en un procedimiento que usa upvar (y la variable se crea automáticamente) y luego demuestro que la variable existe después de que el procedimiento se haya cerrado.

También tenga en cuenta que no es upvar acerca del acceso a las variables globales — esto generalmente se logra mediante los comandos global y variable; en su lugar, upvar se utiliza para trabajar con variables en lugar de valores . Esto generalmente se necesita cuando necesitamos cambiar algo "en el lugar"; uno de los mejores ejemplos de esto es el comando lappend que acepta el nombre de una variable que contiene una lista y agrega uno o más elementos a esa lista, cambiándola en su lugar. Para lograr esto, pasamos lappend el nombre de una variable, no solo un valor de lista. Ahora compare esto con el comando linsert que acepta un valor, no una variable, por lo que toma una lista y produce otra lista.

Otra cosa a tener en cuenta es que por defecto (en su forma de dos argumentos), upvar enlaces a una variable con el nombre especificado un nivel por encima de la pila, no a una variable global. Es decir, usted puede hacer esto:

proc foo {name value} { 
    upvar $name v 
    set v $value 
} 

proc bar {} { 
    set x "" 
    foo x test 
    puts $x ;# will print "test" 

}

En este ejemplo, el procedimiento de "foo" cambia la variable local al procedimiento de "barra".

Por lo tanto, para hacer más clara la intención, muchas personas prefieren especificar siempre el número de marcos de pila upvar debe "subir", como en upvar 1 $varName v que es el mismo que upvar $varName v pero más claro.

Otra aplicación útil de esto se refiere a variables locales, mediante la especificación de cero niveles de la pantalla para subir — este truco es a veces útil para acceder a más convenientemente las variables de matrices:

proc foo {} { 
    set a(some_long_name) test 
    upvar 0 a(some_long_name) v 
    puts $v ;# prints "test" 
    upvar a(some_even_more_long_name) x 
    if {![info exists x]} { 
    set x bar 
    } 
} 

Como beneficio adicional, tenga en cuenta que upvar también comprende números absolutos de marcos de pila que se especifican utilizando el prefijo "#", y "# 0" significa el alcance global. De esta manera, podría vincularse a una variable global, mientras que el procedimiento en su ejemplo original solo se vincularía a las variables globales si se ejecutara en el ámbito global.

1

Bueno, el upvar en tcl, se usa para definir vars que se usará después de que se ejecute el proceso. Ejemplo:

proc tamp {name1 name2} { 
    upvar 1 $name1 Ronalod 
    upvar 1 $name2 Dom 
    set $Dom "Dom" 
} 

#Call the proc tamp 

tamp $name1 $name2 

#Now we can use the vars Ronalod and Dom 

set all_string "${Ronalod}${Dom}" 

Ahora el número 1 en el comando de seguimiento

upvar 1 $name2 Dom 

indicar el nivel en que puede utilizar los VARs, por ejemplo, si usamos 2

upvar 2 $ nombre2 Dom

así que puedo hacer esto:

proc tamp_two {name1 name2} { 
     # do something ... 
     tamp $name1 $name2 
} 

proc tamp {name1 name2} { 
    upvar 2 $name1 Ronalod 
    upvar 2 $name2 Dom 
    set $Dom "Dom" 
} 

#Call the proc tamp_two 

tamp_two $name1 $name2 

#Now we can use the vars Ronalod and Dom 

set all_string "${Ronalod}${Dom}" 

esto puede ser posible con upvar 2, si mantenemos upvar 1, no funciona.

Espero que esto sea útil para usted.

Cuestiones relacionadas