Pregunta

Realmente no entiendo el punto de la función del mapa. ¿Alguien puede explicar con ejemplos su uso?

¿Hay algún beneficio de rendimiento al usar esto en lugar de un bucle o es solo azúcar?

¿Fue útil?

Solución

Cada vez que desee generar una lista basada en otra lista:

# Double all elements of a list
my @double = map { $_ * 2 } (1,2,3,4,5);
# @double = (2,4,6,8,10);

Dado que las listas se convierten fácilmente por pares en hash, si desea una tabla hash para objetos basada en un atributo particular:

# @user_objects is a list of objects having a unique_id() method
my %users = map { $_->unique_id() => $_ } @user_objects;
# %users = ( $id => $obj, $id => $obj, ...);

Es una herramienta realmente de uso general, solo tiene que comenzar a usarla para encontrar buenos usos en sus aplicaciones.

Algunos pueden preferir el código de bucle detallado para fines de legibilidad, pero personalmente, encuentro que map es más legible.

Otros consejos

En primer lugar, es una forma simple de transformar una matriz: en lugar de decir, por ejemplo,

my @raw_values = (...);
my @derived_values;
for my $value (@raw_values) {
    push (@derived_values, _derived_value($value));
}

puedes decir

my @raw_values = (...);
my @derived_values = map { _derived_value($_) } @raw_values;

También es útil para crear una tabla de búsqueda rápida: en lugar de, por ejemplo,

my $sentence = "...";
my @stopwords = (...);
my @foundstopwords;
for my $word (split(/\s+/, $sentence)) {
    for my $stopword (@stopwords) {
       if ($word eq $stopword) {
           push (@foundstopwords, $word);
       }
    }
}

se podría decir

my $sentence = "...";
my @stopwords = (...);
my %is_stopword = map { $_ => 1 } @stopwords;
my @foundstopwords = grep { $is_stopword{$_} } split(/\s+/, $sentence);

También es útil si desea derivar una lista de otra, pero no necesita tener una variable temporal que satura el lugar, p. ej. en lugar de

my %params = ( username => '...', password => '...', action => $action );
my @parampairs;
for my $param (keys %params) {
    push (@parampairs, $param . '=' . CGI::escape($params{$param}));
}
my $url = $ENV{SCRIPT_NAME} . '?' . join('&', @parampairs);

dices lo mucho más simple

my %params = ( username => '...', password => '...', action => $action );
my $url = $ENV{SCRIPT_NAME} . '?'
    . join('&', map { $_ . '=' . CGI::escape($params{$_}) } keys %params);

(Editar: se corrigió el " keys% params " en esa última línea)

La función map se usa para transformar listas. Es básicamente azúcar sintáctico para reemplazar ciertos tipos de for[each] bucles. Una vez que lo rodee, verá usos en todas partes:

my @uppercase = map { uc } @lowercase;
my @hex       = map { sprintf "0x%x", $_ } @decimal;
my %hash      = map { $_ => 1 } @array;
sub join_csv { join(',', map {'"' . $_ . '"' } @_ }

Vea también la Schwartzian transform para el uso avanzado del mapa.

También es útil para hacer hashes de búsqueda:

my %is_boolean = map { $_ => 1 } qw(true false);

es equivalente a

my %is_boolean = ( true => 1, false => 1 );

No hay muchos ahorros allí, pero supongamos que desea definir %is_US_state?

El

mapa se usa para crear una lista transformando los elementos de otra lista.

grep se utiliza para crear una lista filtrando elementos de otra lista.

ordenar se usa para crear una lista ordenando los elementos de otra lista.

Cada uno de estos operadores recibe un bloque de código (o una expresión) que se utiliza para transformar, filtrar o comparar elementos de la lista.

Para mapa , el resultado del bloque se convierte en uno (o más) elemento (s) en la nueva lista. El elemento actual tiene un alias de $ _.

Para grep , el resultado booleano del bloque decide si el elemento de la lista original se copiará en la nueva lista. El elemento actual tiene un alias de $ _.

Para sort , el bloque recibe dos elementos (con alias de $ a y $ b) y se espera que devuelva uno de -1, 0 o 1, lo que indica si $ a es mayor, igual o menos de $ b.

La Transformación de Schwartzian usa estos operadores para almacenar en caché los valores (propiedades) de manera eficiente en la clasificación una lista, especialmente cuando calcular estas propiedades tiene un costo no trivial.

Funciona creando una matriz intermedia que tiene como elementos referencias de matriz con el elemento original y el valor calculado por el cual queremos ordenar. Esta matriz se pasa a ordenar, que compara los valores ya calculados, creando otra matriz intermedia (esta ordenada) que a su vez se pasa a otro mapa que arroja los valores almacenados en caché, restaurando así la matriz a sus elementos de lista iniciales (pero en el orden deseado ahora).

Ejemplo (crea una lista de archivos en el directorio actual ordenados por el momento de su última modificación):

@file_list = glob('*');
@file_modify_times = map { [ $_, (stat($_))[8] ] } @file_list;
@files_sorted_by_mtime = sort { $a->[1] <=> $b->[1] } @file_modify_times;
@sorted_files = map { $_->[0] } @files_sorted_by_mtime;

Al encadenar los operadores, no se necesita ninguna declaración de variables para las matrices intermedias;

@sorted_files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, (stat($_))[8] ] } glob('*');

También puede filtrar la lista antes de ordenar insertando un grep (si desea filtrar en el mismo valor en caché):

Ejemplo (una lista de los archivos modificados en las últimas 24 horas ordenó la última hora de modificación):

    @sorted_files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } grep { $_->[1] > (time - 24 * 3600 } map { [ $_, (stat($_))[8] ] } glob('*');

La función de mapa ejecuta una expresión en cada elemento de una lista y devuelve los resultados de la lista. Digamos que tenía la siguiente lista

@names = ("andrew", "bob", "carol" );

y quería poner en mayúscula la primera letra de cada uno de estos nombres. Podría recorrerlos y llamar a ucfirst de cada elemento, o simplemente podría hacer lo siguiente

@names = map (ucfirst, @names);

La función de mapa es una idea del paradigma de programación funcional. En la programación funcional, las funciones son objetos de primera clase, lo que significa que se pueden pasar como argumentos a otras funciones. El mapa es un ejemplo simple pero muy útil de esto. Toma como argumentos una función (llamémosla f) y una lista l. map tiene que ser una función que tome un argumento, y map simplemente aplica <=> a cada elemento de la lista <=>. <=> puede hacer lo que sea necesario para cada elemento: agregue uno a cada elemento, cuadre cada elemento, escriba cada elemento en una base de datos o abra una ventana del navegador web para cada elemento, que resulta ser una URL válida.

La ventaja de usar <=> es que encapsula muy bien la iteración sobre los elementos de la lista. Todo lo que tiene que hacer es decir & Quot; do <=> a cada elemento, y depende de <=> decidir la mejor manera de hacerlo. Por ejemplo, <=> puede implementarse para dividir su trabajo entre múltiples hilos, y sería totalmente transparente para la persona que llama.

Tenga en cuenta que <=> no es en absoluto específico para Perl. Es una técnica estándar utilizada por lenguajes funcionales. Incluso se puede implementar en C usando punteros de función, o en C ++ usando & Quot; objetos de función & Quot ;.

" Solo azúcar " es duro Recuerde, un bucle es solo azúcar: if y goto pueden hacer todo lo que las construcciones de bucle hacen y más.

Map es una función de nivel suficientemente alto que te ayuda a mantener operaciones mucho más complejas en tu cabeza, para que puedas codificar y depurar problemas más grandes.

Parafraseando a " Programación efectiva de Perl " por Hall & amp; Schwartz se puede abusar del mapa, pero creo que es mejor usarlo para crear una nueva lista a partir de una lista existente.

Crea una lista de los cuadrados de 3,2, & amp; 1:

@numbers = (3,2,1);
@squares = map { $_ ** 2 } @numbers;

Generar contraseña:

$ perl -E'say map {chr(32 + 95 * rand)} 1..16'
# -> j'k=$^o7\l'yi28G

Utiliza map para transformar una lista y asignar los resultados a otra lista, grep para filtrar una lista y asignar los resultados a otra lista. El & Quot; otro & Quot; La lista puede ser la misma variable que la lista que está transformando / filtrando.

my @array = ( 1..5 );
@array = map { $_+5 } @array;
print "@array\n";
@array = grep { $_ < 7 } @array;
print "@array\n";

Se usa cada vez que desea crear una nueva lista a partir de una lista existente.

Por ejemplo, podría asignar una función de análisis en una lista de cadenas para convertirlas en enteros.

Le permite transformar una lista como una expresión en lugar de en declaraciones . Imagine un hash de soldados definido así:

{ name          => 'John Smith'
, rank          => 'Lieutenant'
, serial_number => '382-293937-20'
};

entonces puede operar en la lista de nombres por separado.

Por ejemplo,

map { $_->{name} } values %soldiers

es una expresión . Puede ir a cualquier lugar donde se permita una expresión, excepto que no puede asignarla.

${[ sort map { $_->{name} } values %soldiers ]}[-1]

indexa la matriz, tomando el máximo.

my %soldiers_by_sn = map { $->{serial_number} => $_ } values %soldiers;

Creo que una de las ventajas de las expresiones operativas es que reduce los errores que provienen de las variables temporales.

Si el Sr. McCoy quiere filtrar todos los Hatfields para su consideración, puede agregar esa verificación con una codificación mínima.

my %soldiers_by_sn 
    = map  { $->{serial_number}, $_ } 
      grep { $_->{name} !~ m/Hatfield$/ } 
      values %soldiers
      ;

Puedo seguir encadenando estas expresiones para que si mi interacción con estos datos tiene que llegar a un nivel profundo para un propósito particular, no tengo que escribir mucho código que pretenda que voy a hacer mucho más.

Como otros han dicho, el mapa crea listas a partir de listas. Piense en & Quot; mapeo & Quot; los contenidos de una lista a otra. Aquí hay un código de un programa CGI para tomar una lista de números de patentes e imprimir hipervínculos a las solicitudes de patentes:

my @patents = ('7,120,721', '6,809,505', '7,194,673');
print join(", ", map { "<a href=\"http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL&p=1&u=/netahtml/srchnum.htm&r=0&f=S&l=50&TERM1=$_\">$_</a>" } @patents);

Como otros han dicho, el mapa es más útil para transformar una lista. Lo que no se ha mencionado es la diferencia entre el mapa y un & Quot; equivalente & Quot; en bucle.

Una diferencia es que for no funciona bien para una expresión que modifica la lista sobre la que se repite. Uno de estos termina y el otro no:

perl -e '@x=("x"); map { push @x, $_ } @x'
perl -e '@x=("x"); push @x, $_ for @x'

Otra pequeña diferencia es que el contexto dentro del bloque de mapa es un contexto de lista, pero el bucle for imparte un contexto vacío.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top