perltutorial
Hue-Bond
<p><small>Translated to spanish from [id://23317]'s original [id://66677].</small></p>
<h1>Ámbito</h1>
<p>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 <tt>my</tt> y <tt>local</tt> lo que hace tropezar a la gente, aunque aclarar eso es uno de mis objetivos.</p>
<p>He aprendido mucho de <a href="http://perl.plover.com/FAQs/Namespaces.html">Coping with scoping</a> y de varios libros de Perl (por ejemplo, <a href="http://www.perlmonks.org/?node=Effective%20Perl%20Programming">Effective Perl Programming</a>), 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 <tt>perldoc perlop</tt> y <tt>perldoc -f foo</tt> en tu propio sistema.</p>
<h3>Resumen</h3>
<p>Sí, al principio...</p>
<ul>
<li><tt>my</tt> proporciona ámbito léxico; una variable declarada con <tt>my</tt> sólo es visible en el bloque en que ha sido declarada.</li>
<li>Los bloques de código son trozos delimitados por llaves { }. Un archivo también se considera un bloque.</li>
<li>Usar <tt>use vars qw([nombres de variables])</tt> o <tt>our ([nombres de variables])</tt> para crear globales.</li>
<li><tt>local</tt> 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.</li>
</ul>
<h2>Espacios de nombres</h2>
<p>Una de las ideas básicas, aunque no es necesario dominarla para muchos programas, es la de <i>espacios de nombres</i>. Las variables globales (las que no se declaran con <tt>my</tt>) están en un paquete. Los paquetes proporcionan <i>espacios de nombres</i>, 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).</p>
<p>Por supuesto, estas personas también tienen <i>apellidos</i> (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.</p>
<p>Se usa el operador <tt>package</tt> para cambiar el paquete actual. Cuando usamos <tt>package Garcia</tt> 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".</p>
<p>De forma implícita, hay un <tt>package main</tt> 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 <tt>my</tt>) estarán en <tt>main</tt>. 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).</p>
<p>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:</p>
<code>#!/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</code>
<p>El nombre completo de la variable <tt>$Robert</tt> es <tt>$Szywiecki::Robert</tt> (nótese que el <tt>$</tt> se desplaza al principio, antes del nombre del paquete, indicando que este es el escalar <tt>Robert</tt> que está en el paquete <tt>Szywiecki</tt>). Para el código y, más importante, las subrutinas del paquete <tt>Szywiecki</tt>, un <tt>$Robert</tt> sin calificar se refiere a <tt>$Szywiecki::Robert</tt> -- <i>excepto</i> que <tt>$Robert</tt> haya sido "enmascarado" por una declaración <tt>my</tt> o <tt>local</tt> (hablaremos de esto después).</p>
<p>Ahora, al hacer <tt>use strict</tt> (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 <tt>terminate</tt> fallaría si la descomentáramos. Perl espera encontrar una subrutina <tt>terminate</tt>
en el paquete <tt>main</tt>, pero no la hemos definido. Es decir, esto:</p>
<code>#!/usr/bin/perl -w
use strict;
$Robert = "el jefe"; # error!
print "\$Robert = $Robert\n";</code>
<p>producirá un error, mientras que si ponemos el nombre entero (recordando que existe un <tt>package main</tt> implícito), no hay problema:</p>
<code>#!/usr/bin/perl -w
use strict;
$main::Robert = "el jefe";
print "\$main::Robert = $main::Robert\n";</code>
<p>Para satisfacer a <tt>strict 'vars'</tt> (la parte de <tt>strict</tt> 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:</p>
<ol>
<li>El operador <tt>our ($foo, $bar)</tt> (en Perl 5.6.0 y superiores) declara <tt>$foo</tt> como una variable en el paquete actual.</li>
<li><tt>use vars qw($foo $bar)</tt> (versiones anteriores, pero todavía funciona en 5.6) le dice a <tt>strict 'vars'</tt> que es correcto usar estas variables sin calificarlas del todo.</li>
</ol>
<p>Una de las diferencias entre <tt>our</tt> y el más antiguo <tt>use vars</tt> es que <tt>our</tt> proporciona <i>ámbito léxico</i> (más acerca de esto en la sección de <tt>my</tt>, más abajo).</p>
<p>Otra diferencia es que con <tt>use vars</tt>, debemos usar un array de <i>nombres</i> de variables, no las variables propiamente dichas (tal como con <tt>our</tt>). Ambos mecanismos nos permiten usar globales al mismo tiempo que mantenemos uno de los principales beneficios de <tt>strict 'vars'</tt>: el estar protegidos de crear variables accidentalmente si nos equivocamos al teclear. <tt>strict 'vars'</tt> 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.</p>
<p>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 <i>cualquier parte del código</i>, 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 <tt>strict</tt> por brevedad):</p>
<code>#!/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, 'Robert' es $PoolHall::Robert\n";
__OUTPUT__
Aquí en el trabajo, 'Robert' es el jefe, pero en la piscina, 'Robert' es el experto en dardos</code>
<p>¿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 <tt>terminate</tt> en un ejemplo anterior es <tt>&Szywiecki::terminate</tt> -- lo mismo sirve para hashes y arrays, por supuesto).</p>
<h2><tt>my</tt> (y un poco más sobre <tt>our</tt>) <i>a.k.a</i> ámbito léxico</h2>
<p>Las variables declaradas con <tt>my</tt> no son globales, aunque pueden actuar como tales. Uno de los usos principales de <tt>my</tt> 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 <tt>my</tt>:</p>
<ul>
<li>El ámbito de una variable <tt>my</tt> es un <i>bloque</i> de código.</li>
<li>Un bloque se define normalmente con llaves { }, pero en lo que a Perl concierne, un archivo también es un bloque.</li>
<li>Las variables declaradas con <tt>my</tt> <i><b>no pertenecen a ningún paquete</b></i>, sólo "pertenecen" a su bloque.</li>
<li>Aunque podemos dar nombre a los bloques (por ejemplo, <tt>BEGIN</tt>), no podemos calificar el nombre del bloque para acceder a la variable <tt>my</tt>.</li>
<li>Las variables <tt>my</tt> a nivel de archivo son las que se declaran en un archivo pero fuera de un bloque de código.</li>
<li>No se puede acceder a una variable <tt>my</tt> de archivo desde fuera del archivo en que se declare (<i>excepto</i> que sea el valor de retorno de una subrutina, por ejemplo).</li>
</ul>
<p>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.</p>
<p>He aquí un programa comentado para explicar algunas:</p>
<code>#!/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 ve 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. Pero 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 visible
# si no hubiéramos hecho la declaración arriba, esto provocaría un error: 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!</code>
<p>Tal como la parte de abajo del ejemplo nos dice, dado que no están en ningún paquete, las variables <tt>my</tt> <i>pueden ser</i> visibles incluso aunque hayamos declarado un paquete nuevo, <i>dado que el bloque de código es el archivo</i> (al menos en este ejemplo).</p>
<p>Este ejemplo usa un bloque sin nombre, no hay estructura de control asociada (por ejemplo <tt>if</tt> o <tt>while</tt>). Pero de ser así tampoco habría diferencias.</p>
<p>Las variables <tt>my</tt> 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, <tt>subroutine</tt> se hubiera definido en otro archivo, tendríamos un error en tiempo de ejecución. Una vez sabemos cómo funciona <tt>my</tt>, 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, <tt>use vars</tt> y el nuevo operador <tt>our</tt> difieren: si ponemos <tt>our $foo</tt> en el paquete <tt>Bar</tt> pero <i>fuera de un bloque</i>, estamos diciendo que (hasta que aparezca otro operador de ámbito) debe entenderse que las ocurrencias de <tt>$foo</tt> se refieren a <tt>$Bar::foo</tt>. Esto ilustra la diferencia entre <tt>use vars</tt> y el nuevo <tt>our</tt>:</p>
<code>#!/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";</code>
<p>El segundo <tt>print</tt> produce un error, porque <tt>$carol</tt> se toma como <tt>$Movie::carol</tt>, mientras que <tt>$bob</tt> es <tt>$main::bob</tt>.</p>
<p>Mientras que esta "expansión sobre paquetes" (que sólo se muestra en el caso de <tt>our</tt>) es una similaridad funcional entre los dos tipos distintos de operadores de ámbito, no debemos olvidar la diferencia entre ellos, que es que <tt>our</tt> declara una global, pero <tt>my</tt> no.</p>
<h2><tt>local</tt> <i>a.k.a.</i> ámbito dinámico</h2>
<p>Ahora llegamos a <tt>local</tt>, que es como <tt>my</tt>, pero debido a su nombre, su función se confunde con frecuencia con la de <tt>my</tt>. Aquí está el detalle: <tt>local $foo</tt> <i>almacena</i> el valor actual de la variable <b>global</b> <tt>$foo</tt>, y hace que en el bloque actual y en el código al que se llame desde el bloque actual, <tt>$foo</tt> se refiera al valor que le demos en tal bloque (hacer <tt>local $foo</tt> le dará a <tt>$foo</tt> el valor <tt>undef</tt>, lo mismo que con <tt>my</tt>). Actualmente, <tt>local</tt> sólo funciona en <b>globales</b>, no se puede usar sobre una variable <tt>my</tt>.</p>
<p>Ya que <tt>local</tt> puede afectar a cosas que ocurren fuera del bloque en que lo hemos usado, <tt>local</tt> proporciona ámbito denominado <i>dinámico</i>, 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 <tt>local</tt> 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 <tt>my</tt> y <tt>our</tt>, que tienen efectos visibles en tiempo de compilación.</p>
<p>El resultado básico de esta diferencia es que si <tt>local</tt>izamos una variable dentro de un bloque y llamamos a una subrutina desde ese bloque, la subrutina verá el valor de la variable <tt>local</tt>izada. Esta es una diferencia importante entre <tt>my</tt> y <tt>local</tt>. Comparar el ejemplo anterior con este:</p>
<code>#!/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'</code>
<p>Nótese que <tt>showfoo</tt> ignora (en apariencia) la declaración <tt>my</tt> de <tt>mysub</tt> (ya que hemos abandonado el bloque en el que la declaración <tt>my</tt> tiene efecto) pero la declaración <tt>local</tt> de <tt>localsub</tt> no se ignora. Y después de abandonar ese bloque, el valor original de <tt>$foo</tt> se vuelve a ver.</p>
<p>Espero que hayáis aprendido tanto al leer esto como yo al escribirlo!</p>