matriz - print array perl
¿Cómo leo dos elementos a la vez en un bucle foreach de Perl? (18)
Configure algunos datos de prueba e indique:
use Modern::Perl;
use List::AllUtils qw''zip'';
my @array = zip @{[''a''..''z'']}, @{[1..26]} ;
Bucle simple usando una variable de incremento.
{
my $i = 0;
while(
(my($a,$b) = @array[$i++,$i++]),
$i <= @array # boolean test
){
say "$a => $b";
}
}
Looping sobre pares usando List::Pairwise
(pair)
.
use List::Pairwise qw''pair'';
for my $pair (pair @array){
my($a,$b) = @$pair;
say "$a => $b";
}
Looping sobre la matriz 2 a la vez, usando List::MoreUtils
(natatime)
.
use List::AllUtils qw''natatime'';
my $iter = natatime 2, @array;
while( my($a,$b) = $iter->() ){
say "$a => $b";
}
Forzarlo en un hash, y recorrer las teclas. Útil si no te importa el orden.
{
my %map = @array;
for my $key (keys %map){
my $value = $map{$key};
say "$key => $value";
}
}
Lo que estoy buscando es algo así como:
@list = qw(1 2 3 4 5 6);
foreach (@list) {
#perl magic goes here
print "i: $i, j:$j/n";
}
devoluciones:
i:1, j:2
i:3, j:4
i:5, j:6
En respuesta a una muy buena sugerencia a continuación, debo especificar que este script se ejecutará en el servidor de compilación de otra persona, y no puedo usar ningún módulo de CPAN. Solo Perl estándar.
¿Qué tal una solución funcional de propósito general?
use Carp; # so mapn can croak about errors
sub mapn (&$@) {
my ($sub, $n, @ret) = splice @_, 0, 2;
croak ''$_[1] must be >= 1'' unless $n >= 1;
while (@_) {
local *_ = /$_[0];
push @ret, $sub->(splice @_, 0, $n)
}
@ret
}
sub by ($@) {mapn {[@_]} shift, @_}
sub every ($@); *every = /&by;
La función mapn
funciona igual que map
, excepto que el primer argumento después de su bloque es la cantidad de elementos a tomar. Coloca el primer elemento en $_
y todos los elementos en @_
.
print mapn {"@_/n"} 2 => 1 .. 5;
# prints
1 2
3 4
5
Los siguientes dos subs idénticos, by
y every
crean adverbios útiles para las diversas construcciones de bucle. Procesan la lista con mapn y devuelven una lista de referencias de matriz del tamaño deseado
print "@$_/n" for every 2 => 1..10;
print map {"@$_/n"} grep {$_->[1] > 5} by 2 => 1..10;
Encuentro que esta es una solución más limpia e intuitiva que natatime, u otras soluciones únicas como el estilo de CA para loop.
Arriesgando la etiqueta de nigromancia, decidí agregar una más de la mochila de Tim Toady:
for (0 .. $#list) {
next if $_ % 2;
my ($i, $j) = @list[$_, $_ + 1];
say "i:$i, j:$j";
}
No destructivo, sin listas duplicadas, sin variables de estado y razonablemente concisa.
Como explica Mirod, no hay mucho código para eso. Aquí hay prácticamente todo lo que necesitarías. (Tenga en cuenta que no tengo ningún control para las listas impares o similares).
#!/usr/bin/env perl
use strict;
use warnings;
my @list = qw/1 2 3 4 5 6/;
my $get_em = get_by(2, @list);
while ( my ($i, $j) = $get_em->() ) {
print "i: $i, j: $j/n";
}
sub get_by {
my $n = shift;
my @list = @_;
return sub {
return splice @list, 0, $n;
}
}
Creé este código para resolver un requisito similar:
sub map_pairs(&/@) {
my $op = shift;
use vars ''@array'';
local *array = shift; # make alias of calling array
return () unless @array;
# Get package global $a, $b for the calling scope
my ($caller_a, $caller_b) = do {
my $pkg = caller();
no strict ''refs'';
/*{$pkg.''::a''}, /*{$pkg.''::b''};
};
# Get index counter size.
my $limit = $#array/2;
# Localize caller''s $a and $b
local(*$caller_a, *$caller_b);
# This map is also the return value
map {
# assign to $a, $b as refs to caller''s array elements
(*$caller_a, *$caller_b) = /($array[$_], $array[$_+1]);
$op->(); # perform the transformation
}
map { 2 * $_ } 0..$limit; # get indexes to operate upon.
}
Lo usas así:
@foo = qw( a 1 b 2 c 3 );
my @bar = map_pairs { "$a is $b" } @foo;
Llegar:
@bar = ( ''a is 1'', ''b is 2'', ''c is 3'' );
He querido enviar al mantenedor de List :: MoreUtils, pero no tengo una versión XS para ofrecer.
Creo que la forma correcta de hacerlo es usar tiempo nata, de List::MoreUtils :
de los documentos:
natatime LISTA DE BLOQUES
Crea un iterador de matriz, para recorrer una matriz en fragmentos de
$n
elementos a la vez. (n
a la vez, ¿entiendes?). Un ejemplo es probablemente una mejor explicación de la que podría dar en palabras.
Ejemplo:
my @x = (''a'' .. ''g'');
my $it = natatime 3, @x;
while (my @vals = $it->())
{
print "@vals/n";
}
Esto imprime
a b c d e f g
La implementación de List::MoreUtils::natatime
:
sub natatime ($@)
{
my $n = shift;
my @list = @_;
return sub
{
return splice @list, 0, $n;
}
}
Creo que querrías hacer esto de otra manera. Prueba esto:
while (scalar(@list) > 0) {
$i = shift(@list);
$j = shift(@list);
print "i: $i, j:$j/n";
}
Tenga en cuenta que esto destruirá la lista, pero funcionará para ese pequeño bucle.
El equivalente más cercano es, desafortunadamente, ir a la vieja escuela:
for(my $ix = 0; $ix <= $#list; $ix += 2) {
my $i = $list[$ix];
my $j = $list[$ix + 1];
print "i: $i, j:$j/n";
}
Me gusta más la respuesta de Jack M, realmente, aunque la escribiría en Perl, más sexy:
while(@list) {
my $i = shift @list;
my $j = shift @list;
print "i: $i, j:$j/n";
}
Es probable que desee crear una subrutina simple para que funcione para usted.
Sugiero esto:
{
my $cl_ind = 0;
sub arrayeach(@) {
my @obj = @_;
if(($cl_ind+2) > @obj)
{
$cl_ind = 0;
return;
}
$cl_ind+=2;
return ($obj[$cl_ind-2],$obj[$cl_ind-1]);
}
}
El cierre lo hace funcionar limpiamente. Para usar arrayeach (que funciona como hash cada uno sin requerir una coerción peligrosa a una matriz:
my @temp = (1,2,3,4,5,6,1,2,3,4,5,6);
while( ($a,$b) = arrayeach(@temp)) {
print "A $a AND $b/n";
}
Esto no es destructivo
Esto se puede hacer de forma no destructiva, con List::Gen
simplemente fantástica de List::Gen
:
perl -MList::Gen=":utility" -E ''@nums = "1" .. "6" ;
say "i:$_->[0] j:$_->[1]" for every 2 => @nums''
Salida :
i:1 j:2
i:3 j:4
i:5 j:6
Editar (agregue una versión sin CPAN):
Array slices y C-style para loop à la brian d foy y Tom Christiansen ! Esto se puede leer como "usar un índice ( $i
) para recorrer un @list
foreach
$n
elementos a la vez":
use v5.16; # for strict, warnings, say
my @list = "1" .. "6";
my $n = 2 ; # the number to loop by
$n-- ; # subtract 1 because of zero index
foreach (my $i = 0 ; $i < @list ; $i += $n ) {
say "i:", [ @list[$i..$i+$n] ]->[0], " j:", [ @list[$i..$i+$n] ]->[1];
$i++ ;
}
Accedemos a los resultados como elementos ( ->[0]
) de una matriz anónima ( [ ]
). Para un resultado más genérico, la porción de matriz interpolada se puede usar por sí misma, por ejemplo : print "@list[$i..$i+$n]";
cambiando el valor de $n
según sea necesario.
Si solo pudiera usar Perl estándar sin módulos, probablemente bajaría a un estilo C para el ciclo que cuenta por 2:
for( my $i = 0; $i < @array; $i += 2 ) { my( $i, $j ) = @array[ $i, $i+1 ]; ... }
Sin embargo, si quiere algo sofisticado de uno de los módulos que no puede usar, simplemente puede agregar ese módulo a su código. Si puedes escribir código, puedes usar módulos. Puede que tenga que incluir el módulo con todo el código que entrega mientras configura adecuadamente @INC
. Esta es la idea básica de inc::Module::Install y PAR .
Paso mucho tiempo trabajando con un sistema de compilación que crea su propio repositorio CPAN, instala sus dependencias desde su CPAN privado y luego prueba el código. Tener una granja de compilación no impide el uso de módulos; es la política local lo que hace. Sin embargo, eso podría no tener sentido en todos los casos, aunque sea posible.
Usar un bucle for hará lo que necesites.
use strict;
use warnings;
my @list = qw(1 2 3 4 5 );
my $i = 0;
for ($i = 0; $i < scalar(@list); $i++)
{
my $a = $list[$i];
my $b = $list[++$i];
if(defined($a)) {
print "a:$a";
}
if(defined($b)) {
print "b:$b";
}
print "/n";
}
editar : Corregí mi publicación para usar la función escalar para recuperar el tamaño de la matriz y también agregar algunas comprobaciones en caso de que la matriz no contenga un número par de elementos.
Yo usaría empalme.
my @list = qw(1 2 3 4 5 6);
while(my ($i,$j) = splice(@list,0,2)) {
print "i: $i, j: $j/n";
}
aquí hay una implementación de natatime que no hace una copia de la lista:
sub natatime {
my $n = shift;
my $list = /@_;
sub {
return splice @$list, 0, $n;
}
}
my $it = natatime(3, qw(1 2 3 4 5 6));
while ( my @list = $it->() ) {
print "@list/n";
}
otro enfoque, no totalmente limpio, pero utilizable. cada uno crea un iterador, puede usarlo dos veces. cuando el parámetro es una matriz clásica, devuelve índice y valor, lea esto: https://perldoc.perl.org/functions/each.html
Entonces, tu código puede ser así:
my @array=qw(one two three four five); #five element as unpaired will be ignored
while (my ($i1,$one,$i2,$two)=(each(@array),each(@array)) {
#we will use $ix for detect end of array
next unless defined $i1 and defined $i2; #secure complete end of array
print "fetched array elements: $one => $two/n";
};
El ejemplo anterior no destruirá los datos de origen, contra desplazamiento o similar. Espero que esto sea útil para cualquiera. por supuesto, el caso con iterador simple es mucho mejor.
solución rápida para matrices pequeñas:
for ( map {$_*2} 0..@list/2-1 ){
my ($i, $j) = @list[$_,$_+1];
print "i: $i, j:$j/n";
}
algún tipo de oneliner
datos:
@v = (a=>1, b=>2, c=>3);
esta
print join '', '', map{sprintf ''%s:%s'', $v[$_], $v[$_+1]} grep {!($_%2)} 0..$#v
o algo así como esto
print join '', '', map {sprintf ''%s:%s'', @v[$_,$_+1]} map {$_*2} 0..@v/2-1;
el resultado es el mismo
a:1, b:2, c:3
Creo que una forma más simple es usar viejos pobres ''cada''. Directamente como esto:
while (my ($key,$value) = each @list) {
print "$key=$value/n";
}
Actualizado:
Sí, está mal. Uno debería convertir la lista al hash primero, pero podría ser demasiado explícito:
my %hash = (@list);
while (my ($key,$value) = each %hash) {
print "$key=$value/n";
}
my $i;
for ( qw(a b c d) ) {
if (!defined($i)) { $i = $_; next; }
print STDOUT "i = $i, j = $_/n";
undef($i);
}
Productos:
i = a, j = b
i = c, j = d
También funciona para listas, no solo para matrices.