/ / ¿Cómo hacer que este bucle anidado continúe en perl? - perl, hash

¿Cómo hacer que este bucle anidado continúe en perl? - perl, hash

Aquí está mi código:

#!perl -w
use strict;

my %hash = (
1 => "a",
2 => "b",
);

foreach my $num ( keys %hash ) {
while (<DATA>) {
s/$num/$hash{$num}/g;
print;
}
}
__DATA__
121212
11111
222

Tengo la intención de reemplazar todos los números con los valores correspondientes que existen en el hash. Pero da como resultado:

a2a2a2
aaaaa
222
Hit any key to close this window...

¿Por qué el bucle foreach se ejecuta una sola vez? ¿Quién me lo puede explicar? ¿Y cómo debo cambiar el código? Quiero que salga:

ababab
aaaaa
bbb

Gracias por adelantado.

Respuestas

5 para la respuesta № 1

La razón por la foreach El bucle parece ejecutarse solo una vez porque está leyendo de <DATA> una línea a la vez. La segunda vez que recorre el bucle externo, no quedan más datos para leer de los datos en su bucle interno. En su lugar, ¿por qué no lee todos los <DATA> en una lista primero:

@mylist = <DATA>

Y luego recorra esta lista en su bucle interno.


1 para la respuesta № 2

En lugar de recorrer el archivo para cada clave en el hash, o sorber el archivo, puede combinar las claves del hash en un patrón de búsqueda.

Tenga en cuenta que en la versión actual de su secuencia de comandos, así como en Respuesta de @Benj, el orden en el que se aplican las sustituciones es indeterminado en que el pedido de llaves devuelto por llaves puede diferir en perls o incluso en diferentes ejecuciones usando el mismo perl.

Lo que significa que debe elegir un pedido yquédate con él a menos que te gusten las sorpresas. El siguiente guión combina ambas ideas. Decidí poner las claves más largas antes que las más cortas, lo que tiene sentido en la mayoría de los contextos.

#!perl -w
use strict;

my %hash = qw(1 a 11 zz 2 b);

my $pat = join "|",
map qr/Q$_E/,
sort { length $b <=> length $a }
keys %hash
;

while (<DATA>) {
s/($pat)/$hash{$1}/g;
print;
}

__DATA__
121212
11111
222

Salida:

ababab zzzza bbb

1 para la respuesta № 3

El problema con su código es lo que señala Benj, que el DATA el identificador de archivo está en eof para la segunda iteración de foreach. Pero también que imprima los valores durante la primera iteración.

Si va a reemplazar solo un carácter por otro, es decir, la longitud de su clave / valores nunca es mayor que 1, creo que debería usar tr/// en lugar.

use strict;
use warnings;

while (<DATA>) {
tr/12/ab/;
print;
}

__DATA__
121212
11111
222

El operador de transliteración reemplazará todos los caracteres de la cadena de una sola vez. El único problema es que tr/// El operador debe estar codificado de forma rígida, por lo que no hay cambios dinámicos de caracteres.

Si desea utilizar un hash, debe tomar en serio la precaución de Sinan si su clave / valores pueden ser más largos que 1. Por ejemplo, si tiene ambas claves 1 y 11, ¿cuál debería tener prioridad sobre el otro? Sin embargo, si se trata de reemplazos de un solo carácter, esta es una forma de hacerlo sin problemas:

my %hash = qw(1 a 2 b);

while (<DATA>) {
s|(.)| $hash{$1} // $1 |ge;
print;
}

Tenga en cuenta el uso del modificador de expresiones regulares /e, hace que la sustitución evalúe el RHS e inserte el valor de retorno. En este caso, utilizamos el // operador. Lo que hace es verificar si el valor hash está definido, y si no, simplemente usa la clave en su lugar. Similar a

if (defined $hash{$1}) {
$hash{$1};
} else {
$1;
}

O

defined $hash{$1} ? $hash{$1} : $1

Tenga en cuenta también que, dado que se necesita un carácter a la vez con (.), solo funcionará para teclas de un solo carácter.


0 para la respuesta № 4

La respuesta más simple es simplemente hacer el foreach dentro de while, como

#!perl -w
use strict;

my %hash = (
1 => "a",
2 => "b",
);

while (<DATA>) {
foreach my $num ( keys %hash ) {
s/$num/$hash{$num}/g;
}
print;
}
__DATA__
121212
11111
222

Como han señalado otros, el problema con el programa original era la posición de lectura del DATA filehandle. La rewind La función se usa normalmente para restablecer la posición de un identificador de archivo al principio para que se pueda leer nuevamente, pero en el caso específico de DATA no funciona correctamente, debido a la forma en que el identificador es en realidad solo el identificador de archivo interno de Perl en el .pl archivo en sí. Sin embargo, puede guardar la posición usando tell y luego restablecer allí para cada iteración usando seek:

#!perl -w
use strict;
use Fcntl qw( SEEK_SET );

my %hash = (
1 => "a",
2 => "b",
);

my $pos = tell DATA;

foreach my $num ( keys %hash ) {
seek DATA, $pos, SEEK_SET;
while (<DATA>) {
s/$num/$hash{$num}/g;
print;
}
}
__DATA__
121212
11111
222

Da la salida:

a2a2a2
aaaaa
222
1b1b1b
11111
bbb