Question

Ne pas vraiment obtenir le point de la fonction de la carte. Quelqu'un peut-il expliquer par des exemples son utilisation?

Y a-t-il des avantages en termes de performances à utiliser ceci au lieu d’une boucle ou est-ce juste du sucre?

Était-ce utile?

La solution

Chaque fois que vous souhaitez générer une liste en fonction d'une autre liste:

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

Étant donné que les listes sont facilement converties par paires en hachages, si vous souhaitez une table de hachage pour les objets basés sur un attribut particulier:

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

C’est un outil vraiment polyvalent, il suffit de commencer à l’utiliser pour trouver de bonnes utilisations dans vos applications.

Certains préfèreront peut-être utiliser du code de boucle détaillé pour des raisons de lisibilité, mais personnellement, je trouve map plus lisible.

Autres conseils

Tout d’abord, c’est un moyen simple de transformer un tableau: au lieu de dire, par exemple.

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

vous pouvez dire

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

C'est également utile pour créer une table de recherche rapide: plutôt que, par exemple.

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

vous pourriez dire

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

Cela est également utile si vous souhaitez dériver une liste d'une autre, mais que vous n'avez pas particulièrement besoin d'avoir une variable temporaire encombrant l'endroit, par exemple. plutôt que

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

vous dites le beaucoup plus simple

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

(Édition: correction des " clés% params & manquantes dans la dernière ligne)

La fonction map est utilisée pour transformer des listes. Il s’agit essentiellement d’un sucre syntaxique pour remplacer certains types de boucles for[each]. Une fois que vous l'aurez compris, vous verrez des utilisations partout:

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

Voir aussi la transformation de Schwartzian pour une utilisation avancée de la carte.

C'est également pratique pour faire des hachages de recherche:

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

est équivalent à

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

Il n'y a pas beaucoup d'économies là-bas, mais supposons que vous vouliez définir %is_US_state?

La

carte permet de créer une liste en transformant les éléments d’une autre liste.

grep permet de créer une liste en filtrant les éléments d'une autre liste.

sort permet de créer une liste en triant les éléments d'une autre liste.

Chacun de ces opérateurs reçoit un bloc de code (ou une expression) utilisé pour transformer, filtrer ou comparer des éléments de la liste.

Pour carte , le résultat du bloc devient un (ou plusieurs) élément (s) dans la nouvelle liste. L'élément actuel est associé à $ _.

Pour grep , le résultat booléen du bloc décide si l'élément de la liste d'origine sera copié dans la nouvelle liste. L'élément actuel est associé à $ _.

Pour le tri , le bloc reçoit deux éléments (alias de $ a et $ b) et doit renvoyer l'un des éléments suivants: -1, 0 ou 1, indiquant si $ a est supérieur, égal ou égal à. moins que $ b.

La transformation de Schwartzian utilise ces opérateurs pour mettre en cache efficacement les valeurs (propriétés) à utiliser pour le tri. une liste, en particulier lors du calcul de ces propriétés, a un coût non négligeable.

Cela fonctionne en créant un tableau intermédiaire qui a comme éléments des références de tableau avec l’élément original et la valeur calculée par laquelle nous voulons trier. Ce tableau est passé à trier, ce qui compare les valeurs déjà calculées, créant un autre tableau intermédiaire (celui-ci est trié) qui est à son tour transmis à une autre carte qui jette les valeurs en cache, restaurant ainsi le tableau à ses éléments de liste initiaux (mais dans l'ordre souhaité maintenant).

Exemple (crée une liste de fichiers dans le répertoire actuel triés par date de dernière modification):

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

En enchaînant les opérateurs, aucune déclaration de variables n'est nécessaire pour les tableaux intermédiaires;

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

Vous pouvez également filtrer la liste avant le tri en insérant un grep (si vous souhaitez filtrer sur la même valeur mise en cache):

Exemple (une liste des fichiers modifiés au cours des dernières 24 heures triés avec la dernière heure de modification):

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

La fonction map exécute une expression sur chaque élément d'une liste et renvoie les résultats de la liste. Disons que j'avais la liste suivante

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

et j'ai voulu mettre en majuscule la première lettre de chacun de ces noms. Je pourrais les parcourir en boucle et appeler ucfirst de chaque élément, ou simplement procéder comme suit

@names = map (ucfirst, @names);

La fonction map est une idée du paradigme de la programmation fonctionnelle. En programmation fonctionnelle, les fonctions sont des objets de première classe, ce qui signifie qu'elles peuvent être transmises en tant qu'arguments à d'autres fonctions. La carte en est un exemple simple mais très utile. Il prend comme arguments une fonction (appelons-la f) et une liste l. map doit être une fonction prenant un argument, et map applique simplement <=> à chaque élément de la liste <=>. <=> peut faire le nécessaire pour chaque élément: ajoutez un élément à chaque élément, écrasez chaque élément, écrivez chaque élément dans une base de données ou ouvrez une fenêtre de navigateur Web pour chaque élément, qui se trouve être une URL valide.

L’avantage d’utiliser <=> est qu’il encapsule bien les itérations sur les éléments de la liste. Tout ce que vous avez à faire est de dire & <; à chaque élément, et il appartient à <=> de décider de la meilleure façon de le faire. Par exemple, <=> peut être mis en œuvre pour répartir son travail entre plusieurs threads, ce qui serait totalement transparent pour l'appelant.

Notez que <=> n'est pas du tout spécifique à Perl. C'est une technique standard utilisée par les langages fonctionnels. Il peut même être implémenté en C en utilisant des pointeurs de fonction, ou en C ++ en utilisant des & "Objets de fonction &";.

& "Juste du sucre &" est dur. Rappelez-vous qu’une boucle n’est que du sucre - si et ce qui est fait peut tout faire, les constructions en boucle font plus, et plus encore.

La carte est une fonction de niveau suffisamment élevé pour vous aider à conserver dans votre tête des opérations beaucoup plus complexes, afin que vous puissiez coder et déboguer des problèmes plus importants.

Pour paraphraser " Programmation Perl effective " par Hall & amp; Schwartz, La carte peut être utilisée de manière abusive, mais je pense qu’il est préférable de créer une nouvelle liste à partir d’une liste existante.

Créez une liste des carrés de 3,2, & amp; 1:

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

Générer le mot de passe:

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

Vous utilisez map pour transformer une liste et affecter les résultats à une autre liste, grep pour filtrer une liste et affecter les résultats à une autre liste. Le & Quot; autre & Quot; list peut être la même variable que la liste que vous êtes en train de transformer / filtrer.

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

Il est utilisé à tout moment pour créer une nouvelle liste à partir d'une liste existante.

Par exemple, vous pouvez mapper une fonction d'analyse sur une liste de chaînes pour les convertir en nombres entiers.

Il vous permet de transformer une liste en tant qu’expression expression plutôt qu’en instructions . Imaginez un hachage de soldats défini comme suit:

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

vous pouvez alors utiliser la liste des noms séparément.

Par exemple,

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

est une expression . Il peut aller n'importe où une expression est autorisée - sauf que vous ne pouvez pas l'attribuer.

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

indexe le tableau en prenant le max.

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

Je trouve que l'un des avantages des expressions opérationnelles est qu'elles suppriment les erreurs provenant des variables temporaires.

Si M. McCoy souhaite filtrer tous les Hatfields pour examen, vous pouvez ajouter cette vérification avec un codage minimal.

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

Je peux continuer à enchaîner ces expressions afin que, si mon interaction avec ces données doive atteindre un objectif particulier, je n'ai pas à écrire beaucoup de code prétendant en faire beaucoup plus.

Comme d'autres l'ont dit, map crée des listes à partir de listes. Pensez à & Quot; mapping & Quot; le contenu d'une liste dans une autre. Voici un code d'un programme CGI permettant de dresser une liste de numéros de brevets et d'imprimer des hyperliens vers les demandes de brevet:

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

Comme d'autres l'ont dit, map est le plus utile pour transformer une liste. Ce qui n’a pas été mentionné, c’est la différence entre map et un & Quotient & Quot; pour la boucle.

Une différence est que for ne fonctionne pas bien pour une expression qui modifie la liste en itérant. L’un d’eux se termine et l’autre ne le fait pas:

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

Une autre petite différence est que le contexte à l'intérieur du bloc de carte est un contexte de liste, mais que la boucle for crée un contexte vide.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top