7
votes

Quel est le moyen le plus rapide de compter le nombre de mots dans une chaîne dans Perl?

J'ai quelques fonctions que je rencontre plus d'un million de fois sur divers textes, ce qui signifie que de petites améliorations dans ces fonctions se traduisent par de gros gains dans l'ensemble. Actuellement, j'ai remarqué que toutes mes fonctions qui impliquent des chiffres de mots prennent considérablement plus de temps que tout le reste, alors je pense que je veux essayer de faire du nombre de mots de manière différente.

Fondamentalement, quelle est ma fonction Saisissez un certain nombre d'objets qui ont un texte associé à eux, vérifiez que ce texte ne correspond pas à certains modèles, puis comptez le nombre de mots dans ce texte. Une version de base de la fonction est la suivante: xxx

Je fais beaucoup de comparaisons de texte similaires à ce que je fais ici ailleurs dans mon code, alors je suppose que mon Le problème doit être avec mon mot comptage. Y a-t-il un moyen plus rapide de le faire que de fractionnement sur \ s + ? Si oui, qu'est-ce que c'est et pourquoi est-ce plus rapide (je peux donc comprendre ce que je fais mal et peut appliquer cette connaissance à des problèmes similaires plus tard).


0 commentaires

5 Réponses :


1
votes

Étant donné que vous n'avez besoin que du nombre de mots au lieu de la matrice de mots, il serait bon d'éviter d'utiliser divisé code>. Quelque chose que cela pourrait fonctionner: xxx pré>

Il remplace le travail de construction d'un tableau de mots avec le travail de substituer chaque mot avec lui-même. Vous auriez besoin de le repasser pour voir s'il est plus rapide. P>

Edit: Cela peut être plus rapide: P>

++$num_words while $text =~ /\S+/g;


0 commentaires

14
votes

Utiliser une boucle tandis que avec une regex est la manière la plus rapide que j'ai trouvée pour compter les mots: xxx

La boucle tandis que la boucle est plus rapide car la mémoire n'a pas besoin d'être allouée pour chacun des trouvé des mots. De plus, la regex est dans le contexte booléen, ce qui signifie qu'il n'a pas besoin d'extraire la correspondance réelle de la chaîne.


2 commentaires

Agréable! Merci! C'est génial.


Formidable "pourquoi" aller avec le "quoi". Génial que vous avez souligné Benchmark pour une utilisation dans d'autres expérimentations et optimisation.



2
votes

Depuis que vous limitez le nombre de mots à 30, vous pouvez revenir de la fonction précédente: xxx pré>

ou en utilisant divisé code>: p>

$num_words = () = split /\s+/, $text, 30;


1 commentaires

Définitivement. J'ai intégré cela dans la réponse de @eric Strom.



2
votes

Pour l'exactitude, de Réponse d'Aleroot , vous voulez probablement divisé" ", pas l'original Split / \ s + / Pour éviter une erreur de clôture : a "Split" sur "/ \ s + /" est comme une "Split (" ")" Sauf que toute personne de premier plan produit un premier champ nul. * Cette différence vous donnera un mot supplémentaire (le premier champ NULL, c'est-à-dire) par ligne.

Pour la vitesse, car vous appuyez sur le nombre de mots à 30, vous souhaitez probablement utiliser la limite argument * : divisé" ", $ str, 30 . .

D'autre part, d'autres réponses vous détournent judicieusement de Splitt complètement, car vous n'avez pas besoin de la liste des mots, juste leur compte.


2 commentaires

Un mot supplémentaire par ligne avec des espaces de tête


Oui, bonne correction. Sur une ligne avec des espaces de premier plan, non sur aucune ligne du tout.



5
votes

Si les mots ne sont séparés que par des espaces uniques, les espaces de comptage sont rapides. XXX PREP> Benchmark mis à jour: P>

my $text = 'asdf asdf asdf asdf asdf';

sub count_array {
   my @text_words = split(/\s+/, $text);
   scalar(@text_words);
}

sub count_list {
   my $x =()= $text =~ /\S+/g;       #/
}

sub count_while {
   my $num; 
   $num++ while $text =~ /\S+/g;     #/
   $num
}

sub count_tr {
    1 + ($text =~ tr{ }{ });
}

say count_array; # 5
say count_list;  # 5
say count_while; # 5
say count_tr; # 5

use Benchmark 'cmpthese';

cmpthese -2 => {
    array => \&count_array,
    list  => \&count_list,
    while => \&count_while,
    tr    => \&count_tr,
}

#            Rate  list while array    tr
# list   220911/s    --  -24%  -44%  -94%
# while  291225/s   32%    --  -26%  -92%
# array  391769/s   77%   35%    --  -89%
# tr    3720197/s 1584% 1177%  850%    --


1 commentaires

Sauf si vous utilisez / D, la liste de remplacement des paramètres par défaut de la liste de saisie, donc tr / // est équivalent à tr {} {}