Domanda

In realtà non si ottiene il punto della funzione mappa. Qualcuno può spiegare con esempi il suo uso?

Ci sono dei vantaggi prestazionali nell'usarlo al posto di un loop o è solo zucchero?

È stato utile?

Soluzione

Ogni volta che vuoi generare un elenco basato su un altro elenco:

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

Poiché gli elenchi possono essere facilmente convertiti in coppie in hash, se si desidera una tabella hash per oggetti basata su un attributo particolare:

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

È uno strumento davvero generico, devi solo iniziare a usarlo per trovare buoni usi nelle tue applicazioni.

Alcuni potrebbero preferire un codice di loop dettagliato per scopi di leggibilità, ma personalmente trovo map più leggibile.

Altri suggerimenti

Prima di tutto, è un modo semplice di trasformare un array: piuttosto che dire ad esempio

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

puoi dire

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

È anche utile per creare una tabella di ricerca rapida: anziché ad esempio

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

potresti dire

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

È utile anche se desideri derivare un elenco da un altro, ma non è particolarmente necessario che una variabile temporanea ingombra il luogo, ad es. anziché

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);

dici molto più semplice

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

(Modifica: risolto il " chiavi% params " nell'ultima riga)

La funzione map viene utilizzata per trasformare elenchi. È fondamentalmente zucchero sintattico per sostituire alcuni tipi di for[each] loop. Una volta che ci avvolgerai la testa, ne vedrai gli usi ovunque:

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

Vedi anche la Transform di Schwartzian per un uso avanzato della mappa.

È anche utile per creare hash di ricerca:

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

è equivalente a

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

Non ci sono molti risparmi lì, ma supponiamo che tu voglia definire %is_US_state?

mappa viene utilizzato per creare un elenco trasformando gli elementi di un altro elenco.

grep viene utilizzato per creare un elenco filtrando gli elementi di un altro elenco.

ordina viene utilizzato per creare un elenco ordinando gli elementi di un altro elenco.

Ognuno di questi operatori riceve un blocco di codice (o un'espressione) che viene utilizzato per trasformare, filtrare o confrontare elementi dell'elenco.

Per mappa , il risultato del blocco diventa uno (o più) elementi nel nuovo elenco. L'elemento corrente è impostato su $ _.

Per grep , il risultato booleano del blocco decide se l'elemento dell'elenco originale verrà copiato nel nuovo elenco. L'elemento corrente è impostato su $ _.

Per ordinamento , il blocco riceve due elementi (con alias $ ae $ b) e dovrebbe restituire uno di -1, 0 o 1, indicando se $ a è maggiore, uguale o meno di $ b.

La Schwartzian Transform utilizza questi operatori per memorizzare in modo efficiente valori (proprietà) nella cache da utilizzare nell'ordinamento un elenco, specialmente quando si calcolano queste proprietà, ha un costo non banale.

Funziona creando un array intermedio che ha come elementi riferimenti di array con l'elemento originale e il valore calcolato in base al quale vogliamo ordinare. Questo array viene passato all'ordinamento, che confronta i valori già calcolati, creando un altro array intermedio (questo è ordinato) che a sua volta viene passato a un'altra mappa che elimina i valori memorizzati nella cache, ripristinando così l'array ai suoi elementi di elenco iniziali (ma nell'ordine desiderato ora).

Esempio (crea un elenco di file nella directory corrente ordinati per ora dell'ultima modifica):

@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;

Incatenando gli operatori insieme, non è necessaria alcuna dichiarazione di variabili per gli array intermedi;

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

Puoi anche filtrare l'elenco prima dell'ordinamento inserendo un grep (se vuoi filtrare sullo stesso valore memorizzato nella cache):

Esempio (un elenco dei file modificati nelle ultime 24 ore ha ordinato l'ultima ora di modifica):

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

La funzione mappa esegue un'espressione su ciascun elemento di un elenco e restituisce i risultati dell'elenco. Diciamo che avevo il seguente elenco

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

e volevo scrivere in maiuscolo la prima lettera di ciascuno di questi nomi. Potrei collegarli in loop e chiamare prima di ogni elemento, oppure potrei semplicemente fare quanto segue

@names = map (ucfirst, @names);

La funzione mappa è un'idea del paradigma di programmazione funzionale. Nella programmazione funzionale, le funzioni sono oggetti di prima classe, il che significa che possono essere passate come argomenti ad altre funzioni. Mappa è un esempio semplice ma molto utile di questo. Prende come argomenti una funzione (chiamiamola f) e un elenco l. map deve essere una funzione che accetta un argomento e la mappa si applica semplicemente <=> a ogni elemento dell'elenco <=>. <=> può fare tutto ciò che è necessario per ogni elemento: aggiungerne uno a ogni elemento, quadrare ogni elemento, scrivere ogni elemento in un database o aprire una finestra del browser Web per ogni elemento, che risulta essere un URL valido.

Il vantaggio di usare <=> è che incapsula bene gli iteranti sugli elementi dell'elenco. Tutto quello che devi fare è dire & Quot; fai <=> su ogni elemento, e spetta a <=> decidere il modo migliore per farlo. Ad esempio <=> potrebbe essere implementato per dividere il suo lavoro tra più thread e sarebbe totalmente trasparente per il chiamante.

Nota che <=> non è affatto specifico per Perl. È una tecnica standard utilizzata dai linguaggi funzionali. Può anche essere implementato in C usando i puntatori a funzione, o in C ++ usando & Quot; oggetti funzione & Quot ;.

" Solo zucchero " è duro. Ricorda, un ciclo è solo zucchero - if's e goto possono fare tutto ciò che i costrutti di ciclo fanno e altro ancora.

La mappa è una funzione di livello abbastanza alto che ti aiuta a tenere in mente operazioni molto più complesse, in modo da poter codificare ed eseguire il debug di problemi più grandi.

Per parafrasare " Efficace programmazione Perl " di Hall & amp; Schwartz, è possibile abusare della mappa, ma penso che sia meglio utilizzarla per creare un nuovo elenco da un elenco esistente.

Crea un elenco dei quadrati di 3,2, & amp; 1:

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

Genera password:

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

Usa map per trasformare un elenco e assegnare i risultati a un altro elenco, grep per filtrare un elenco e assegnare i risultati a un altro elenco. Il & Quot; altro & Quot; L'elenco può essere la stessa variabile dell'elenco che stai trasformando / filtrando.

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

Viene utilizzato ogni volta che desideri creare un nuovo elenco da un elenco esistente.

Ad esempio, è possibile mappare una funzione di analisi su un elenco di stringhe per convertirle in numeri interi.

Ti consente di trasformare un elenco come espressione piuttosto che in istruzioni . Immagina un hash di soldati definito così:

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

quindi puoi operare sulla lista dei nomi separatamente.

Ad esempio,

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

è un'espressione . Può andare ovunque sia consentita un'espressione, tranne che non è possibile assegnarla.

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

indicizza l'array, prendendo il massimo.

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

Trovo che uno dei vantaggi delle espressioni operative sia che riduce i bug che provengono da variabili temporanee.

Se Mr. McCoy vuole filtrare tutti gli Hatfields per considerazione, puoi aggiungere quel controllo con una codifica minima.

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

Posso continuare a concatenare queste espressioni in modo che se la mia interazione con questi dati deve arrivare in profondità per uno scopo particolare, non devo scrivere molto codice che finge di fare molto di più.

Come altri hanno già detto, la mappa crea liste da liste. Pensa a & Quot; mappatura & Quot; il contenuto di un elenco in un altro. Ecco un po 'di codice da un programma CGI per prendere un elenco di numeri di brevetto e stampare collegamenti ipertestuali alle domande di brevetto:

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);

Come altri hanno già detto, la mappa è molto utile per trasformare un elenco. Ciò che non è stato menzionato è la differenza tra la mappa e un & Quot; equivalente & Quot; per loop.

Una differenza è che per non funziona bene per un'espressione che modifica l'elenco su cui scorre. Uno di questi termina e l'altro no:

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

Un'altra piccola differenza è che il contesto all'interno del blocco della mappa è un contesto di elenco, ma il ciclo for impartisce un contesto vuoto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top