Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Ámbito de variables en Perl: lo básico

by Hue-Bond (Priest)
on Jul 03, 2006 at 16:13 UTC ( [id://559011]=perltutorial: print w/replies, xml ) Need Help??

Translated to spanish from arturo's original Variable Scoping in Perl: the basics.

Ámbito

Una de las cosas necesarias para dominar Perl es cómo manejar los mecanismos de ámbito que te ofrece. ¿Que queremos globales? Las tenemos. ¿Que queremos evitar "colisiones" (dos variables con el mismo nombre pisándose una a la otra)? Podemos, y hay más de una forma de conseguirlo. Pero las reglas de ámbito de Perl no son siempre tan claras, y no es sólo la diferencia entre my y local lo que hace tropezar a la gente, aunque aclarar eso es uno de mis objetivos.

He aprendido mucho de Coping with scoping y de varios libros de Perl (por ejemplo, Effective Perl Programming), por lo que debo dar crédito a sus autores (Dominus por el primero, y Joseph N. Hall y merlyn por el segundo). Dominus también ha hecho varias correcciones a los errores (algunos de ellos notables) de una versión anterior de este tutorial, así que él debería considerarse como mínimo el segundo autor (N del T: aunque Dominus no está de acuerdo con esto). Sin embargo la documentación que viene con tu versión de Perl es la más actualizada que puedes consultar, así que no dudes en usar perldoc perlop y perldoc -f foo en tu propio sistema.

Resumen

Sí, al principio...

  • my proporciona ámbito léxico; una variable declarada con my sólo es visible en el bloque en que ha sido declarada.
  • Los bloques de código son trozos delimitados por llaves { }. Un archivo también se considera un bloque.
  • Usar use vars qw([nombres de variables]) o our ([nombres de variables]) para crear globales.
  • local guarda el valor de una global y lo sustituye por un valor nuevo a efectos del código que está en el bloque actual y al que llamemos desde tal bloque.

Espacios de nombres

Una de las ideas básicas, aunque no es necesario dominarla para muchos programas, es la de espacios de nombres. Las variables globales (las que no se declaran con my) están en un paquete. Los paquetes proporcionan espacios de nombres, que voy a explicar usando como metáfora los apellidos. En los países de habla hispana, "Roberto" es un nombre bastante común, así que es posible que conozcamos a más de un "Roberto" (asumiendo que vivimos en uno de estos países). Normalmente, para los humanos, el contexto de la conversación basta para que nuestra audiencia sepa de qué "Roberto" estamos hablando (en el vestíbulo de la piscina, "Roberto" es el que controla de dardos; pero en el trabajo "Roberto" es el director de la empresa).

Por supuesto, estas personas también tienen apellidos (pero existen personas distintas con el mismo apellido, así que después de todo esta metáfora no es perfecta), y si quisiéramos ser explícitos podríamos añadirlos para que quien nos oye sepa de qué "Roberto" hablamos. $Garcia::Roberto es una cosa distinta de $Gonzalez::Roberto. Cuando tenemos dos variables distintas con el mismo "nombre de pila", podemos referirnos a cualquiera de ellas, sin importar el lugar del código en que nos encontremos, usando el nombre completo de la variable.

Se usa el operador package para cambiar el paquete actual. Cuando usamos package Garcia en el programa, estamos, en efecto, diciendo que todas las variables y funciones no calificadas (es decir, que no tienen "apellido" explícito) deben ser entendidas como si estuvieran en el paquete Garcia. Es como decir "en esta parte del programa, voy a hablar de la familia Garcia".

De forma implícita, hay un package main al principio de los programas, esto es, excepto que declaremos explícitamente un paquete distinto, todas las variables que se declaren (teniendo en cuenta el uso de my) estarán en main. A las variables que están en un paquete se les llama, y con razón, "globales de paquete", porque se puede acceder a ellas sin más desde todos los operadores y subrutinas que están en tal paquete (y si somos explícitos con sus nombres, también son accesibles desde fuera de él).

Usar paquetes hace que acceder a las variables sea como moverse en distintos círculos. Por ejemplo, en el trabajo, se entiende que "Roberto" es "Roberto Szywiecki", el jefe. En la piscina, "Roberto" es "Roberto Yamauchi", el experto en dardos. Aquí tenemos un pequeño programa para mostrar el uso de paquetes:

#!/usr/bin/perl -w package Szywiecki; $Robert = "el jefe"; sub terminate { my $name = shift; print "$Robert ha despedido a ${name}\n"; } terminate("arturo"); # muestra "el jefe ha despedido a arturo" package main; # terminate("arturo"); # produce un error si se descomenta __OUTPUT__ el jefe ha despedido a arturo

El nombre completo de la variable $Robert es $Szywiecki::Robert (nótese que el $ se desplaza al principio, antes del nombre del paquete, indicando que este es el escalar Robert que está en el paquete Szywiecki). Para el código y, más importante, las subrutinas del paquete Szywiecki, un $Robert sin calificar se refiere a $Szywiecki::Robert -- excepto que $Robert haya sido "enmascarado" por una declaración my o local (hablaremos de esto después).

Ahora, al hacer use strict (y se debería! consulta strict.pm por ejemplo), tendremos que declarar todas esas variables globales antes de poder usarlas, EXCEPTO que querramos usar siempre sus nombres completos. Esa es la razón por la que la segunda llamada a terminate fallaría si la descomentáramos. Perl espera encontrar una subrutina terminate en el paquete main, pero no la hemos definido. Es decir, esto:

#!/usr/bin/perl -w use strict; $Robert = "el jefe"; # error! print "\$Robert = $Robert\n";

producirá un error, mientras que si ponemos el nombre entero (recordando que existe un package main implícito), no hay problema:

#!/usr/bin/perl -w use strict; $main::Robert = "el jefe"; print "\$main::Robert = $main::Robert\n";

Para satisfacer a strict 'vars' (la parte de strict que se encarga de las declaraciones de variables), tenemos dos opciones; producen resultados distintos y una de ellas sólo está disponible en Perl 5.6.0 y más recientes:

  1. El operador our ($foo, $bar) (en Perl 5.6.0 y superiores) declara $foo como una variable en el paquete actual.
  2. use vars qw($foo $bar) (versiones anteriores, pero todavía funciona en 5.6) le dice a strict 'vars' que es correcto usar estas variables sin calificarlas del todo.

Una de las diferencias entre our y el más antiguo use vars es que our proporciona ámbito léxico (más acerca de esto en la sección de my, más abajo).

Otra diferencia es que con use vars, debemos usar un array de nombres de variables, no las variables propiamente dichas (tal como con our). Ambos mecanismos nos permiten usar globales al mismo tiempo que mantenemos uno de los principales beneficios de strict 'vars': el estar protegidos de crear variables accidentalmente si nos equivocamos al teclear. strict 'vars' exige que las variables se declaren explícitamente (como diciendo "estas son las globales que voy a usar"). Los dos mecanismos permiten hacer esto con globales de paquete.

Algo que debemos tener en cuenta (que es potencialmente algo malo, dependiendo de lo fanático que uno sea de la privacidad") es que las variables globales no son sólo globales a ese paquete, sino que son accesibles desde cualquier parte del código, siempre que se usen sus nombres completos. Podemos hablar de Roberto, el experto de dardos, en el trabajo si decimos "Roberto Yamauchi" (en este código no uso strict por brevedad):

#!/usr/bin/perl -w package Szywiecki; $Robert = "el jefe"; package PoolHall; $Robert = "el experto en dardos"; package Szywiecki; # a trabajar otra vez! print "Aquí en el trabajo, 'Robert' es $Robert, pero en la piscina, 'R +obert' es $PoolHall::Robert\n"; __OUTPUT__ Aquí en el trabajo, 'Robert' es el jefe, pero en la piscina, 'Robert' +es el experto en dardos

¿Lo veis? Entender los paquetes no es tan difícil. En términos generales, un paquete es como una familia de variables (¡y de subrutinas! el nombre completo de aquel terminate en un ejemplo anterior es &Szywiecki::terminate -- lo mismo sirve para hashes y arrays, por supuesto).

my (y un poco más sobre our) a.k.a ámbito léxico

Las variables declaradas con my no son globales, aunque pueden actuar como tales. Uno de los usos principales de my es operar con una variable que sólo sirva en un bucle o subrutina, pero desde luego que hay muchos más. He aquí algunos conceptos acerca de my:

  • El ámbito de una variable my es un bloque de código.
  • Un bloque se define normalmente con llaves { }, pero en lo que a Perl concierne, un archivo también es un bloque.
  • Las variables declaradas con my no pertenecen a ningún paquete, sólo "pertenecen" a su bloque.
  • Aunque podemos dar nombre a los bloques (por ejemplo, BEGIN), no podemos calificar el nombre del bloque para acceder a la variable my.
  • Las variables my a nivel de archivo son las que se declaran en un archivo pero fuera de un bloque de código.
  • No se puede acceder a una variable my de archivo desde fuera del archivo en que se declare (excepto que sea el valor de retorno de una subrutina, por ejemplo).

Mientras sólo escribamos programas de un solo archivo (por ejemplo, los que no importan módulos), algunos de estos conceptos no importan mucho. Pero si estamos interesados en privacidad y encapsulación (por ejemplo, si escribimos módulos), tendremos que entender todas esas cosas.

He aquí un programa comentado para explicar algunas:

#!/usr/bin/perl -w use strict; #recordemos que estamos en el paquete main use vars qw($foo); $foo = "Yo!"; # damos valor a $main::foo print "\$foo: $foo\n"; # muestra "Yo!" my $foo = "Hey!"; # variable my a nivel de archivo print "\$foo: $foo\n"; # muestra "Hey!" -- la variable nueva 'pisa' a +la vieja { # comenzamos un bloque my $foo = "Yacht-Z"; print "\$foo: $foo\n"; # muestra "Yacht-Z" -- hay una nueva variable $foo visible print "\$main::foo: $main::foo\n"; # todavía podemos ver $main::foo subroutine(); } # fin del bloque print "\$foo: $foo\n"; # nuestra variable $foo a nivel de archivo se v +e otra vez! print "\$main::foo: $main::foo\n"; # $main::foo todavía está aquí sub subroutine { print "\$foo: $foo\n"; # muestra "Hey!" # ¿Por qué? porque la variable declarada en el bloque sin nombre est +á en # su ámbito -- ahora tenemos otras llaves distintas rodeando esto. P +ero la # variable de archivo todavía está en ámbito, y todavía "pisa" a la # declaración de $main::foo. } package Bar; print "\$foo: $foo\n"; # muestra "Hey!" -- la variable my todavía es v +isible # si no hubiéramos hecho la declaración arriba, esto provocaría un err +or: el # intérprete nos diría que Bar::foo no ha sido definida. __OUTPUT__ $foo: Yo! $foo: Hey! $foo: Yacht-Z $main::foo: Yo! $foo: Hey! $foo: Hey! $main::foo: Yo! $foo: Hey!

Tal como la parte de abajo del ejemplo nos dice, dado que no están en ningún paquete, las variables my pueden ser visibles incluso aunque hayamos declarado un paquete nuevo, dado que el bloque de código es el archivo (al menos en este ejemplo).

Este ejemplo usa un bloque sin nombre, no hay estructura de control asociada (por ejemplo if o while). Pero de ser así tampoco habría diferencias.

Las variables my de archivo SON accesibles desde los bloques definidos en ese archivo (tal como el ejemplo muestra), esta es una manera de la que pueden actuar como globales. Si, no obstante, subroutine se hubiera definido en otro archivo, tendríamos un error en tiempo de ejecución. Una vez sabemos cómo funciona my, podemos saber, sólo fijándonos en la sintaxis del archivo, dónde va a ser visible. Esta es una razón por la que el ámbito que proporciona se llama "léxico". En esto, use vars y el nuevo operador our difieren: si ponemos our $foo en el paquete Bar pero fuera de un bloque, estamos diciendo que (hasta que aparezca otro operador de ámbito) debe entenderse que las ocurrencias de $foo se refieren a $Bar::foo. Esto ilustra la diferencia entre use vars y el nuevo our:

#!/usr/bin/perl -w use strict; our ($bob); use vars qw($carol); $carol = "ted"; $bob = "alice"; print "Bob => $bob, Carol => $carol\n"; package Movie; print "Bob => $bob, Carol => $carol\n";

El segundo print produce un error, porque $carol se toma como $Movie::carol, mientras que $bob es $main::bob.

Mientras que esta "expansión sobre paquetes" (que sólo se muestra en el caso de our) es una similaridad funcional entre los dos tipos distintos de operadores de ámbito, no debemos olvidar la diferencia entre ellos, que es que our declara una global, pero my no.

local a.k.a. ámbito dinámico

Ahora llegamos a local, que es como my, pero debido a su nombre, su función se confunde con frecuencia con la de my. Aquí está el detalle: local $foo almacena el valor actual de la variable global $foo, y hace que en el bloque actual y en el código al que se llame desde el bloque actual, $foo se refiera al valor que le demos en tal bloque (hacer local $foo le dará a $foo el valor undef, lo mismo que con my). Actualmente, local sólo funciona en globales, no se puede usar sobre una variable my.

Ya que local puede afectar a cosas que ocurren fuera del bloque en que lo hemos usado, local proporciona ámbito denominado dinámico, ya que su efecto se determina a partir de lo que ocurre cuando se ejecuta el programa. Esto es, el compilador no puede saber cuando local hará efecto o no durante la compilación del programa (que ocurre antes de la ejecución del mismo). Esto distingue el ámbito dinámico del léxico proporcionado por my y our, que tienen efectos visibles en tiempo de compilación.

El resultado básico de esta diferencia es que si localizamos una variable dentro de un bloque y llamamos a una subrutina desde ese bloque, la subrutina verá el valor de la variable localizada. Esta es una diferencia importante entre my y local. Comparar el ejemplo anterior con este:

#!/usr/bin/perl -w use strict; use vars qw ($foo); # "our $foo" si usamos 5.6 $foo = "global value"; print "\$foo: $foo\n"; # muestra "global value" print "mysub result '", &mysub(), "'\n"; # "global value" print "localsub result '", &localsub(), "'\n"; # "local value" print "no sub result '", &showfoo(), "'\n"; # "global value" sub mysub { my $foo = "my value"; showfoo(); } sub localsub { local $foo = "local value"; showfoo(); # SIEMPRE muestra "local value" } sub showfoo { return $foo; } __OUTPUT__ $foo: global value mysub result 'global value' localsub result 'local value' no sub result 'global value'

Nótese que showfoo ignora (en apariencia) la declaración my de mysub (ya que hemos abandonado el bloque en el que la declaración my tiene efecto) pero la declaración local de localsub no se ignora. Y después de abandonar ese bloque, el valor original de $foo se vuelve a ver.

Espero que hayáis aprendido tanto al leer esto como yo al escribirlo!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perltutorial [id://559011]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (5)
As of 2024-03-19 08:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found