2010-12-11 22 views
5

Quiero eliminar elementos de algunas matrices grandes con una subrutina. Utilizo una referencia para evitar una copia en el submarino.¿Cómo puedo eliminar un elemento de una matriz referenciada?

@a=qw(ok now what is hi the matter); 

sub zonk { 
    $array=shift; # this is a reference of an array 
    foreach $i (0..$#$array) { # I saw some say to avoid last element to get size 
    #if (@$array[$i] =~ /hi/) { delete @$array[$i]; } 
    #if ($array->[$i] =~ /hi/) { delete $array->[$i]; } 
    #if ($array->[$i] =~ /hi/) { delete @$array->[$i]; } 
    if ($array->[$i] =~ /hi/) { print "FOUND "; } 
    print $array->[$i],"\n"; 
    } 
    @$array = grep{$_} @$array; # removes empty elements 
} 
zonk(\@a); 
print join(':',@a); 

Si funciono con el programa anterior al igual que me sale:

ok 
now 
what 
is 
FOUND hi 
the 
matter 
ok:now:what:is:hi:the:matter 

Pero si uso cualquiera de las líneas de comentarios vez me sale:

argumento de eliminación no es un elemento de control o de rebanar en la línea hi.pl 10.

Intenté empalmar originalmente pero luego los índices cambiaban y confundían la iteración. Sería bueno saber todos los métodos mencionados en esta publicación, sin embargo, el más eficiente es lo que estoy buscando :)

Adición: Esto funciona perfectamente (me refiero a cada línea comentada) en mi máquina Linux (ubuntu 9.10, perl 5.10) pero el error anterior está en mi caja de Windows 7 en el trabajo usando perl 5.005_03. La actualización no es una opción.

Gracias

Respuesta

0
sub zonk { 
    $array=shift; # this is a reference of an array 
    foreach $i (0..$#$array) { # I saw some say to avoid last element to get size 

    print $array->[$i],"\n"; 

    if ($array->[$i] =~ /hi/) { 
     delete @{$array}[$i]; 
    } 

    } 
    @$array = grep{$_} @$array; # removes empty elements 
} 
zonk(\@a); 
print join(':',@a); 
+0

No funciona en mi caja de Windows 7 con Perl 5.005. Agregué un apéndice a mi pregunta original. – Shawn

6

Por qué no grep desde el primer momento?

@array = grep { !/hi/ } @array; 
# Or, for *referenced* array 
@$arrayRef = grep { !/hi/ } @$arrayRef; 

Un pequeño conjunto de notas para aclarar las dudas que surgieron en los comentarios:

  1. Este método (o cualquier método que usa grep incluyendo el código de su creador original) se aumentar el uso de la memoria del guión por el tamaño de la nueva matriz resultante.

    E.g. si el script (sin el primer array) tomó 10MB de memoria, el array original tomó 15MB de memoria, y el array resultante tomó 14MB de memoria, luego la huella de memoria total de su programa aumentará de 25MB a 39MB mientras que grep Esta corriendo.

  2. Una vez que el grep comlpetes, la memoria utilizada por la matriz original estará disponible para la recolección de basura (con algunas advertencias irrelevantes para esta publicación).

  3. Sin embargo - y esto es importante - incluso si el original de 15 MB de datos son basura recogida, que 15 MB no será devuelto por Perl para el sistema operativo - por ejemplo, la huella de memoria del script seguirá siendo 39MB y no disminuirá a 24 MB incluso después de la recolección de basura.

  4. Por el lado bueno, ese 15MB liberado estará disponible para la asignación de memoria durante el resto de la vida útil de su programa (dejando de lado los problemas de fragmentación de la memoria), por lo tanto, si su script requerirá asignación adicional de 1MB, 5MB, o 15 MB de memoria, su huella de memoria NO crecerá más allá del punto alto de 39 MB. Y si requiere 17 MB de memoria adicional, la huella de memoria resultante solo será de 41 MB, no de 56 MB.

  5. Si esta memoria aritmética no es satisfactoria para usted (p. Ej.si su matriz original era de 500 MB y no están dispuestos a tolerar la huella de memoria de programa ascendente a 1 GB), entonces Dallaylaen's answer a continuación es una gran algoritmo para hacer la tarea sin asignación de memoria adicional

+0

esto parece ser la respuesta para mí. si no hay uso de memoria adicional ... el comentario a continuación dice que es tan – Shawn

+0

@Shawn - enmendó la respuesta con notas comprensibles re: problemas de memoria. – DVK

2

Si lo hace @$array = grep { ... } @$array de todos modos, ¿por qué no quedarse con grep { $_ !~ /hi/ }?

Sin embargo, si usted es realmente relacionados con la memoria, es posible que trate de ir desde la parte superior:

my $i = @$array; 
while ($i-->0) { 
    splice @$array, $i, 1 if $array->[$i] =~ /hi/; 
}; 

Pero esto tiene un rendimiento peor caso de n^2, por lo que puede ser aún mejor escribe en C-con-dólares en lugar de bienes Perl:

my $array = [qw(ok now what is hi the matter)]; 
my $to = 0; 
# move elements backwards 
for (my $from=0; $from < @$array; $from++) { 
    $array->[$from] =~ /hi/ and next; 
    $array->[$to++] = $array->[$from]; 
}; 
# remove tail 
splice @$array, $to; 
print join ":", @$array; 

Aún no sé qué delete $array->[$i] no va a funcionar, funciona en Perl 5.10 y 5.8 Actualmente tengo en la mano.

+0

Estoy terriblemente mal: Perl reutilizaría la memoria en el caso de @array = grep {...} @array. ¡Ve a @DVK! – Dallaylaen

+0

si Perl reutiliza la memoria con grep, lo usaré con seguridad. me encanta mi unix grep ... – Shawn

+0

es un poco TAD más complicado que eso, pero un resumen lo suficientemente cerca. Voy a publicar un comentario sobre mi propia respuesta a la memoria: en detalle. Sin embargo, tu solución es mi favorita en cuanto a la limitación de memoria: +1. – DVK

4

invertir el orden de su bucle y se puede utilizar splice:

for(my $i = $#array; $i >= 0; --$i) { 
    #... 
} 
+0

genial ... pero me hace pensar de alguna manera que los iteradores en Perl son un poco incómodos. – Shawn

+0

@Shawn: eso es porque no es un "iterador" en el sentido de un objeto que apunta a una lista vinculada. No creo que a Perl le falte un verdadero "iterador" para crear una clase LinkedList a mano. – DVK

+0

@Shawn: básicamente, las matrices Perl se COMPORTAN MUY de cerca con las listas vinculadas, con la excepción de las inserciones/eliminaciones aleatorias en el medio de la lista que no son O (1) como notaron (pero shift/unshift/pop/push son O (1)) – DVK

0

bucle a través de cada llave, empuje cada elemento para eliminarlo en una matriz, a continuación, utilizar una final eliminar - de un solo golpe!

foreach my $key(keys %$my_array) {  
    my $val= $my_array->{$key};  

    if ($val eq "BAD") { 
     push (@unwanted,$key); 
    }    
} 
delete @{$my_array}{@unwanted}; 
Cuestiones relacionadas