• Publicidad

Cómo funciona undef y local

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Cómo funciona undef y local

Notapor pablgonz » 2014-01-17 13:05 @587

Hola a todos, tengo un par de dudas sobre local y undef.

Poseo este script el cual lo utilizo con cuatro opciones diferentes, traté (infructuosamente) de adaptarlo dentro de otro script (script-perl-pst2pdf-y-pdftex-t5369-30.html) con el que me han dado la mano en ocasiones anteriores, que sí utilizo con frecuencia, y me asaltan mis dudas con las maneras que tengo de leer el archivo de entrada (undef, local).

Sé que undef lee todo el archivo en memoria, pero, no sé cómo funciona local. El script es el siguiente:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use v5.14;                                # Versión mínima para que funcione
  3. use File::Basename;                       # Nombre de los archivos de entrada
  4. use Getopt::Long;                         # Leemos la opciones de entrada
  5. use autodie;                              # Vivir con honor o morir con gloria
  6. my $parserin = 0;                         # 1-> Comentamos código archivo entrada
  7. my $parserout = 0;                        # 1-> Descomentamos código archivo entrada
  8. my $tcolorbox = 0;                        # 1-> Reordenamos PSTexample para tcblising
  9. my $showexpl= 0;                          # 1-> Reordenamos PSTexample para swowxpl
  10. my $imageDir = "images";                  # directorio de las imágenes (images por defecto)
  11.  
  12. @ARGV == 0 && die "file name expected!\n";
  13.  
  14. my @SuffixList = (".tex","",".ltx");                    # extensión del archivo de entrada
  15. my ($name,$path,$ext) = fileparse($ARGV[0],@SuffixList);
  16. if ( $ext eq "" ) { $ext = ".tex"; }                    # fijamos la extensión
  17. my $TeXfile = "$path$name$ext";
  18.  
  19. my $name_in_file = shift;
  20. my $name_out_file  = "$name-tmp.tex";
  21.  
  22. my $result = GetOptions ("parserin"  => \$parserin,  # Comentamos el archivo de entrada
  23.                         "imageDir=s" => \$imageDir,  # string
  24.                         "parserout"  => \$parserout, # Descomentamos el archivo de entrada
  25.                         "showexpl"   => \$showexpl,  # Reordenamos para showexpl
  26.                         "tcolorbox"  => \$tcolorbox);# Reordenamos para tcblisiting
  27. if ($parserin) {
  28.     $parserin = 1;
  29. }
  30. if ($parserout) {
  31.     $parserout = 1;
  32. }
  33. if ($showexpl) {
  34.     $showexpl = 1;
  35. }
  36. if ($tcolorbox) {
  37.     $tcolorbox = 1;
  38. }
  39. if ($parserin) {
  40. ## Lectura del archivo
  41. open my $INEXAFILE, '<', $name_in_file;
  42. undef $/;                                                       # modo 'slurp'
  43. my $archivo_entrada = <$INEXAFILE>;
  44. close   $INEXAFILE;
  45.  
  46. ## Partición del documento
  47. my($cabeza,$cuerpo,$final) = $archivo_entrada =~ m/\A (.+? ^\\begin{document}) \s* (.+?) \s* (^ \\end{document}) \s* \z/msx;
  48.  
  49.  
  50. ## Cambios a realizar
  51. my %cambios = (
  52.     '\pspicture'                => '\TRICKS',
  53.     '\endpspicture'             => '\ENDTRICKS',
  54.  
  55.     '\begin{pspicture'          => '\begin{TRICKS',
  56.     '\end{pspicture'            => '\end{TRICKS',
  57.  
  58.     '\postscript'               => '\POSTRICKS',
  59.  
  60.     '\begin{postscript}'        => '\begin{POSTRICKS}',
  61.     '\end{postscript}'          => '\end{POSTRICKS}',
  62. );
  63.  
  64.  
  65. ## Variables y constantes
  66. my $no_del = "\0";
  67. my $del    = $no_del;
  68.  
  69. ## Reglas
  70. my $llaves      = qr/\{ .+? \}                                                                  /x;
  71. my $no_corchete = qr/(?:\[ .+? \])?                                                             /x;
  72. my $delimitador = qr/\{ (?<del>.+?) \}                                                          /x;
  73. my $verb        = qr/verb [*]?                                                                  /ix;
  74. my $lst         = qr/lstinline (?!\*) $no_corchete                                              /ix;
  75. my $mint        = qr/mint      (?!\*) $no_corchete $llaves                                      /ix;
  76. my $marca       = qr/\\ (?:$verb | $lst | $mint) (\S) .+? \g{-1}                                /x;
  77. my $comentario  = qr/^ \s* \%+ .+? $                                                            /mx;
  78. my $definedel   = qr/\\ (?:   DefineShortVerb | lstMakeShortInline  ) $no_corchete $delimitador /ix;
  79. my $indefinedel = qr/\\ (?: UndefineShortVerb | lstDeleteShortInline) $llaves                   /ix;
  80.  
  81.  
  82. ## Cambiar
  83. while ($cuerpo =~
  84.     /   $marca
  85.     |   $comentario
  86.     |   $definedel
  87.     |   $indefinedel
  88.     |   $del .+? $del                                                           # delimitado
  89.     /gimx) {
  90.  
  91.     my($pos_inicial, $pos_final) = ($-[0], $+[0]);                              # posiciones
  92.     my $encontrado = ${^MATCH};                                                 # lo encontrado
  93.  
  94.     given ($encontrado) {
  95.         when (/$definedel/) {                                                   # definimos delimitador
  96.             $del = $+{del};
  97.             $del = "\Q$+{del}" if substr($del,0,1) ne '\\';                     # es necesario "escapar" el delimitador
  98.         }
  99.         when (/$indefinedel/) {                                                 # indefinimos delimitador
  100.             $del = $no_del;
  101.         }
  102.         default {                                                               # Aquí se hacen los cambios
  103.             while (my($busco, $cambio) = each %cambios) {
  104.  
  105.                 $encontrado =~ s/\Q$busco\E/$cambio/g;                          # es necesario escapar $busco, ya que contiene caracteres extraños
  106.             }
  107.  
  108.             substr $cuerpo, $pos_inicial, $pos_final-$pos_inicial, $encontrado; # insertamos los nuevos cambios
  109.  
  110.             pos($cuerpo)= $pos_inicial + length $encontrado;                    # reposicionamos la siguiente búsqueda
  111.         }
  112.     }
  113. }
  114.  
  115.  
  116. ## Escritura del resultado
  117. open my $OUTEXAFILE, '>', $name_out_file;
  118. print   $OUTEXAFILE "$cabeza\n$cuerpo\n$final\n";
  119. close   $OUTEXAFILE;
  120.  
  121. }
  122.  
  123. if ($tcolorbox) {
  124. ## Lectura del archivo
  125. open my $INEXAFILE, '<', $name_in_file;
  126. my $archivo;
  127. {
  128.     local $/;
  129.     $archivo = <$INEXAFILE>;
  130. }
  131. close   $INEXAFILE;
  132.  
  133. ## cambios para tcblisting
  134. $archivo =~
  135. s/(?<inicio>\\begin\{PSTexample\}\[)(?<posicion>above|left) (?<opciones>.*?)(?:\]) \s+
  136. (?<graphics>(?<initpic>\\includegraphics\[ .+? \]\{) (?<picname>.+?) \})\s+(?<end>\\end\{PSTexample\})
  137. /$+{inicio}$+{posicion}$+{opciones}\]
  138. \\lstinputlisting[firstnumber=1,linerange=document-document]{$imageDir\/$+{picname}.tex}
  139. \\tcblower
  140. $+{graphics}
  141. $+{end}/gmxs;
  142.  
  143. $archivo =~
  144. s/(?<inicio>\\begin\{PSTexample\}\[) (?<posicion>below|right) (?<opciones>.*?)(?:\]) \s+
  145. (?<graphics>(?<initpic>\\includegraphics\[ .+? \]\{) (?<picname>.+?) \})\s+(?<end>\\end\{PSTexample\})
  146. /$+{inicio}$+{posicion}$+{opciones}\]
  147. $+{graphics}
  148. \\tcblower
  149. \\lstinputlisting[firstnumber=1,linerange=document-document]{$imageDir\/$+{picname}.tex}
  150. $+{end}/gmxs;
  151.  
  152. ## Grabar
  153. open my $OUTEXAFILE, '>', $name_in_file;
  154. print $OUTEXAFILE $archivo;
  155. close $OUTEXAFILE;
  156. }
  157.  
  158. if ($showexpl) {
  159. ## Lectura del archivo
  160. open my $INEXAFILE, '<', $name_in_file;
  161. my $archivo;
  162. {
  163.     local $/;
  164.     $archivo = <$INEXAFILE>;
  165. }
  166. close   $INEXAFILE;
  167.  
  168. # cambios para showexpl
  169. $archivo =~
  170. s/(?<inicio>\\begin\{PSTexample\})(?:\[)? (?<opciones>.*?):? (?:\])? \s+
  171. (?<graphics>(?<initpic>\\includegraphics)(?<scale>\[ .+? \])(\{)(?<picname>.+?) \})\s+(?<end>\\end\{PSTexample\})
  172. /qq(\\LTXinputExample\[firstnumber=1,linerange=document-document,graphic=\{$+{scale}$imageDir\/$+{picname}}) . ($+{opciones} ? "," : "") . qq($+{opciones}\]
  173. {$imageDir\/$+{picname}.tex\})/gemxs;
  174.  
  175. # Grabar
  176. open my $OUTEXAFILE, '>', $name_in_file;
  177. print $OUTEXAFILE $archivo;
  178. close $OUTEXAFILE;
  179. }
  180. # Vuelvo todo a la normalidad , debo cambiar el nombre del fichero de entrada
  181. if ($parserout) {
  182. ## Lectura del archivo
  183. open my $INEXAFILE, '<', $name_in_file;
  184. undef $/;                                                       # modo 'slurp'
  185. my $archivo_entrada = <$INEXAFILE>;
  186. close   $INEXAFILE;
  187. ## Partición del documento
  188. my($cabeza,$cuerpo,$final) = $archivo_entrada =~ m/\A (.+? ^\\begin{document}) \s* (.+?) \s* (^ \\end{document}) \s* \z/msx;
  189.  
  190. ## Cambios a realizar
  191. my %cambios = (
  192.          '\TRICKS'                              => '\pspicture',
  193.          '\ENDTRICKS'                   => '\endpspicture',
  194.  
  195.          '\begin{TRICKS'                => '\begin{pspicture',
  196.          '\end{TRICKS'                  => '\end{pspicture',
  197.  
  198.          '\begin{POSTRICKS}'    => '\begin{postscript}',
  199.          '\end{POSTRICKS}'              => '\end{postscript}',
  200. );
  201.  
  202.  
  203. ## Variables y constantes
  204. my $no_del = "\0";
  205. my $del    = $no_del;
  206.  
  207. ## Reglas
  208. my $llaves      = qr/\{ .+? \}                                                                  /x;
  209. my $no_corchete = qr/(?:\[ .+? \])?                                                             /x;
  210. my $delimitador = qr/\{ (?<del>.+?) \}                                                          /x;
  211. my $verb        = qr/verb [*]?                                                                  /ix;
  212. my $lst         = qr/lstinline (?!\*) $no_corchete                                              /ix;
  213. my $mint        = qr/mint      (?!\*) $no_corchete $llaves                                      /ix;
  214. my $marca       = qr/\\ (?:$verb | $lst | $mint) (\S) .+? \g{-1}                                /x;
  215. my $comentario  = qr/^ \s* \%+ .+? $                                                            /mx;
  216. my $definedel   = qr/\\ (?:   DefineShortVerb | lstMakeShortInline  ) $no_corchete $delimitador /ix;
  217. my $indefinedel = qr/\\ (?: UndefineShortVerb | lstDeleteShortInline) $llaves                   /ix;
  218.  
  219.  
  220. ## Cambiar
  221. while ($cuerpo =~
  222.     /   $marca
  223.     |   $comentario
  224.     |   $definedel
  225.     |   $indefinedel
  226.     |   $del .+? $del                                                           # delimitado
  227.     /gimx) {
  228.  
  229.     my($pos_inicial, $pos_final) = ($-[0], $+[0]);                              # posiciones
  230.     my $encontrado = ${^MATCH};                                                 # lo encontrado
  231.  
  232.     given ($encontrado) {
  233.         when (/$definedel/) {                                                   # definimos delimitador
  234.             $del = $+{del};
  235.             $del = "\Q$+{del}" if substr($del,0,1) ne '\\';                     # es necesario "escapar" el delimitador
  236.         }
  237.         when (/$indefinedel/) {                                                 # indefinimos delimitador
  238.             $del = $no_del;
  239.         }
  240.         default {                                                               # Aquí se hacen los cambios
  241.             while (my($busco, $cambio) = each %cambios) {
  242.  
  243.                 $encontrado =~ s/\Q$busco\E/$cambio/g;                          # es necesario escapar $busco, ya que contiene caracteres extraños
  244.             }
  245.  
  246.             substr $cuerpo, $pos_inicial, $pos_final-$pos_inicial, $encontrado; # insertamos los nuevos cambios
  247.  
  248.             pos($cuerpo)= $pos_inicial + length $encontrado;                    # reposicionamos la siguiente búsqueda
  249.         }
  250.     }
  251. }
  252.  
  253. ## Escritura del resultado
  254. open my $OUTEXAFILE, '>', $name_in_file;
  255. print   $OUTEXAFILE "$cabeza\n$cuerpo\n$final\n";
  256. close   $OUTEXAFILE;
  257. }
  258.  
Coloreado en 0.009 segundos, usando GeSHi 1.0.8.4
Básicamente lo que hace es leer un archivo , modificar unas palabras, re ordenar unas líneas, y luego volver las palabras modificadas a la normalidad, pero en algunas ocasiones usa undef y en otras local, ¿cuál es la diferencia?

Mi idea era poder ingresar esas cuatro opciones dentro del otro script como subfunciones, modificando los nombres de las variables y las rutas... en fin, quizás no es posible, por el momento me conformo con comprender las diferencias entre local y undef.

Saludos,
Pablo
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Publicidad

Re: Cómo funciona undef y local

Notapor explorer » 2014-01-17 20:28 @895

undef() lo que hace es indefinir una variable, o dicho de otra manera, le asigna el valor 'undef' (indefinido), en el caso de ser una variable escalar. En caso de arrays y hashes, lo que hace es quitarles todos los elementos (vaciarlos), como si se hubieran declarado de nuevo.

Luego, en el programa, necesitamos indefinir la variable $/, ya que de esa manera estamos indicando que el delimitar de registros es 'indefinido', o dicho de otra manera, que no hay delimitar de registros, así que, cuando leamos el primer registro, leeremos todo el archivo de golpe.

Esta operación de indefinir la variable especial $/ es algo peligrosa: afecta a todas las operaciones de lectura que se hagan a partir del momento de la indefinición.

Entonces: si en el programa solo tenemos una operación de lectura, sabemos que si indefinimos la variable con un undef(), afectará a solo esa operación de lectura y nada más... pero si en un programa hay más de una operación de lectura... quizás no querremos realizar una operación "aspiradora" en alguna de ellas, así que tendríamos que indefinir la variable $/ en un sitio, pero no en otro, lo cual es un lío.

En otras situaciones eso se resuelve con la ayuda de unas llaves: si usamos variables locales (con my()) cuando se terminen las llaves, esas variables desaparecerán y no afectarán al resto del código. El problema es que no podemos usar my() con la variable especial $/.

Y ahí es donde entra en juego local().

Esto es lo que dice perldoc -f local: «Un local() modifica las variables listadas para que sean locales en el bloque que les rodea, un archivo o un eval()».

Entonces, un local() dentro de un bloque (unas llaves), hace que las variables sean locales en ese bloque. Y también las inicializa con el valor 'undef'. Cuando el bloque termine, las variables recuperan su valor anterior. Así que, tenemos todo lo que queremos en una sola operación:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $archivo;
  2. open my $ARCHIVO, 'archivo.txt';
  3. {
  4.     local $/;
  5.     $archivo = <$ARCHIVO>;
  6. }
  7. close $ARCHIVO;
  8.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
De esta manera, activamos el modo aspiradora para esa operación de lectura en concreto, y no afectará a las siguientes operaciones de lectura, si las hubiera, ya que $/ recupera su valor al final del bucle.

En tu programa...

Si todas las operaciones de lectura son del tipo "aspiradora", te vale con poner un undef $/ al principio del programa. Ese cambio afectará a todas las lecturas. Y no necesitas más undef() ni más local(). Si, en cambio, hay distintas formas de lectura de archivos, entonces quita todos los undef() y cámbialos por estructuras local() como la que te he puesto.

Mira lo que dice perldoc perlvar:
Debes ser muy cuidadoso cuando modifiques los valores por defecto de la mayor parte de las variables especiales descritas en este documento. En la mayor parte de los casos querrás localizarlas antes de cambiarlas, dado que si no lo haces, el cambio puede afectar a otros módulos que dependan de los valores por defecto de las variables especiales que hayas cambiado. Esta es una de las formas correctas de leer un archivo de una sola vez:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. open my $fh, "<", "foo" or die $!;
  2. local $/; # activa el modo aspiradora localmente
  3. my $content = <$fh>;
  4. close $fh;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Pero el siguiente código es muy malo:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. open my $fh, "<", "foo" or die $!;
  2. undef $/; # activa modo aspiradora
  3. my $content = <$fh>;
  4. close $fh;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

por que algún otro módulo puede querer leer datos de otro archivo en el "modo línea" por defecto, así que si se ha ejecutado el código anterior, el valor global de $/ ha cambiado para cualquier otro código que se ejecute dentro del intérprete Perl.

Normalmente, cuando una variable es localizada, quieres asegurarte que este cambio afecta al ámbito más pequeño posible. Así que a menos que ya estés dentro de un pequeño bloque "{}", debes crear uno. Por ejemplo:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $content = '';
  2. open my $fh, "<", "foo" or die $!;
  3. {
  4.     local $/;
  5.     $content = <$fh>;
  6. }
  7. close $fh;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Cómo funciona undef y local

Notapor pablgonz » 2014-01-17 23:25 @017

Muchas gracias por la explicación, ahora entiendo por qué no podía adaptar algunas partes del código dentro del otro script, cambiaré el código usando local(), puesto que hago varias operaciones de lectura.

Del hilo abrir-archivo-de-texto-y-guardarlo-en-una-variable-escalar-t4379.html propones la siguiente línea (bastante corta):
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $archivo = do { local $/; open FILE, 'fichero,txt'; <FILE> };
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
que es equivalente (supongo), pero, creo que debería ser:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $archivo = do { local $/; open my $FILE, "$name.tex"; <$FILE> };
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
¿estoy en lo correcto?
Saludos
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Re: Cómo funciona undef y local

Notapor explorer » 2014-01-18 10:03 @460

Las dos formas hacen lo mismo. Y en este caso particular, la primera es mejor pero solo porque se escribe con menos caracteres.

Actualmente se recomienda usar variables escalares para almacenar el gestor de archivo, pues de esa manera es fácil llevarlo a distintas partes de un programa, por ejemplo como argumento en una subrutina.

Pero en el caso de una "lectura rápida", en la que vamos a abrir un archivo e inmediatamente leerlo por completo, pues no es necesario.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 21 invitados