2012-07-13 15 views
12

Por lo tanto, esta pregunta es solo para fines de aprendizaje y curiosidad, pero ¿alguien puede explicar cómo funciona la siguiente función?Lista :: Función MoreUtils mesh o 'zip'

sub mesh (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@) { 
    my $max = -1; 
    $max < $#$_ && ($max = $#$_) foreach @_; 
    map { 
     my $ix = $_; 
     map $_->[$ix], @_; 
    } 0 .. $max; 
} 

Es del módulo List::MoreUtils. Lo estoy usando en una de mis aplicaciones y vi el código fuente, ¡y me hizo sentir como si no supiera nada de Perl! ¿Alguien puede explicar esta locura? :) ¡Gracias!

Respuesta

13

No cubriré la parte de prototipos (la mafia dijo que lo haría).

Aquí hay una versión más legible - idealmente, debería explicarse por sí misma

sub mesh (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@) { 

    # Calculate the maximum number of elements in each of the array refs 
    # we were passed: 

    my $maxLength = 0; 
    foreach my $array_ref (@_) { # @_ is all arrey refs passed in 
     if ($maxLength < @$array_ref) { 
      # we found an array longer than all previous ones 
      $maxLength = @$array_ref; 
     } 
    } 

    # If you represent the arrays as a matrix: 
    # ARR1 = [ a1e1, a1e2, .... a1eN], 
    # ARR2 = [ a2e1, a2e2, .... a2eN], 
    # ... 
    # ARR2 = [ aMe1, aMe2, .... aMeN]; 
    # Then, we are simply walking through the matrix; 
    # each column top to bottom then move on to next column left to right 
    # (a1e1, a2e1, ... aMe1, a1e2, a2e2, ... aMeN) 

    my @results; 
    for (my $index = 0; $index < $maxLength; $index++) { # Iterate over columns 
     foreach my $array_ref (@_) { # Iterate over per-row cells in each column 
      push @results, $array_ref->[$index]; 
     } 
    } ; 
} 

aquí es una versión original comentado

sub mesh (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@) { 

    # Calculate the largest index in each of the array refs 
    # @_ is an array of all the input arrayrefs 
    # $_ will be one of the array refs in a foreach loop 
    # $#{$X} is the largest index in arrayref X; thus 
    # $#$_ is the largest index in arrayref $_ 
    my $max = -1; 
    $max < $#$_ && ($max = $#$_) foreach @_; 

    # Return a list, obtained by looping 
    # over every index from 0 to the maximal index of any of the arrays 
    # Then, for each value of the index ($ix), push into the resulting list 
    # an element with that index from each of the arrays. 
    map { 
     my $ix = $_; 
     map $_->[$ix], @_; 
    } 0 .. $max; 
} 


Una de las cosas inusuales en este método es el function signature (prototype).

sub mesh (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@) { 

Como @mob y @ikegami señalado sabiamente en los comentarios,

... It instructs Perl to expect between 2 and 32 named arrays, and to pass them to the function (in @_) as array references. So if you call mesh @a,@b,@c , then @_ in mesh is set to (\@a,\@b,\@c) rather than one "flat" list with all the individual elements of @a, @b, and @c (mob)
... They technically don't need to be named, just dereferenced. e.g. @$ref and @{[qw(foo bar)]} work just as well as @a . In other words, it has to start with @ (and not be a slice). (ikegami)

En otras palabras, los siguientes 2 llamadas se comportan de la misma

my @a1 = (1); 
my @a2 = (2); 
sub mesh_prototype(\@\@) { print "$_->[0]\n" } 
sub mesh_pass_arrayref() { print "$_->[0]\n" } 
mesh_prototype(@a1, @a2); 
mesh_pass_arrayref(\@a1, \@a2); 

Esto se hace para que se puede pasar matrices individuales (y no arrayrefs) como argumentos a funciones que se comportarán como integradas (por ejemplo, map/sort)

Para responder a la pregunta de Zaid cuanto a lo que sucede si 1 o 33 matrices se muestran como parámetros para llamar a mesh(), se generará un error de tiempo de compilación:

Not enough arguments for main::mesh at mesh.pl line 16, near "@a1)" 
Execution of mesh.pl aborted due to compilation errors. 

Too many arguments for main::mesh at mesh.pl line 16, near "@a2)" 
Execution of mesh.pl aborted due to compilation errors. 
+5

Voy a cubrir la parte prototipos :-). Le indica a Perl que espere entre 2 y 32 * matrices con nombre *, y que las pase a la función (en '@ _') como referencias de matriz. Entonces, si llamas 'mesh @ a, @ b, @ c',' @ _' en 'mesh' se establece en' (\ @a, \ @ b, \ @ c) 'en lugar de una lista" plana "con todos los elementos individuales de '@ a',' @ b', y '@ c'. – mob

+2

@mob, Técnicamente no necesitan ser * nombrados *, solo * desreferenciados *. p.ej. '@ $ ref' y' @ {[qw (foo bar)]} 'funcionan tan bien como' @ a'. En otras palabras, tiene que comenzar con '@' (y no ser un corte). – ikegami

+0

@DVK: supongo que es posible que desee cubrir lo que sucede cuando se pasan 33 argumentos :) – Zaid

Cuestiones relacionadas