7
votes

Comment puis-je compter des sous-chaînes qui se chevauchent à Perl?

Je dois mettre en œuvre un programme pour compter la survenue d'une sous-chaîne dans une chaîne de Perl. Je l'ai mis en œuvre comme suit xxx

maintenant c'est ce que je ferais normalement. Cependant, dans la mise en œuvre ci-dessus, je souhaite compter sur l'occurrence de «AA» dans «AAA». Ici, je reçois une réponse comme 1 qui semble raisonnable, mais j'ai besoin d'envisager les cas qui se chevauchent également. Par conséquent, le cas ci-dessus devrait donner une réponse comme 2 car il y a deux 'd'AA si nous envisageons de se chevaucher.

Quelqu'un peut-il suggérer comment implémenter une telle fonction ??


2 commentaires

Vous pouvez utiliser l'appelant "opérateur de goatse" (ce n'est pas vraiment un opérateur) pour obtenir un compte en une seule passe: mon compte $ = () = $ string = ~ / $ substring / g; . Cela ne corrige pas votre détection de sous-chaînes qui se chevauchent. Je vais aller dans le prochain commentaire.


L'opérateur de goatiser ou aller fonctionne car lorsqu'il est évalué dans le contexte scalaire, l'affectation à une liste renvoie le nombre d'éléments fournis pour la mission. Considérez cet exemple: $ foo = () = (1,3,2,9) '; , la liste des numéros est attribuée à la liste vide. Toutes les valeurs sont supprimées, mais l'affectation elle-même est évaluée pour obtenir la valeur pour $ FOO . Étant donné que $ FOO est un scalaire, nous évaluons l'affectation de la liste et obtenez 4 . Ainsi, Go peut être utilisé pour éliminer un tableau temporaire inutile.


6 Réponses :


8
votes

voir La réponse de YSTH ... Je n'ai pas réalisé que Le motif pourrait consister uniquement d'une affirmation de largeur zéro et de travailler encore à cet effet.

Vous pouvez utiliser Lookahead positif comme suggéré par d'autres, et écrivez la fonction comme: p> xxx pré>

Vous pouvez également utiliser POS code> Pour ajuster l'endroit où la recherche suivante prend à partir de: p>

C:\Temp> t
2


1 commentaires

Oui, un peu de malade là-bas; vous attendriez naïvement quelque chose comme / \ B / G pour correspondre à la même position un nombre infini de fois, mais il existe une règle spéciale que les matchs de largeur zéro ne sont pas autorisés deux fois à la même position de départ (même sur scalaire séparé Contexte M // G).



3
votes

Vous pouvez utiliser un Assertion Lookahead dans l'expression régulière:

sub countnmstr {
    my @matches = $_[0] =~ /(?=($_[1]))/g;

    return scalar @matches;
}


0 commentaires

3
votes

Vous pouvez essayer cela, pas plus de regex que nécessaire.

$ ./perl.pl
Total count: 4


1 commentaires

Il n'y a pas besoin de substr ici; Index prend un paramètre 3ème paramètre en option pour le dire où commencer la recherche.



12
votes

Tout le monde se complique assez compliqué dans leurs réponses (d'oh! daoToad aurait dû faire son commentaire une réponse!), peut-être parce qu'ils ont peur de l'opérateur de goaSse. Je ne l'ai pas nommé, c'est ce que les gens l'appellent. Il utilise le truc que le résultat d'une affectation de liste est le nombre d'éléments de la liste des droitstres.

Le Perl Idiom pour compter les correspondances est alors: p>

 my $count = () = 'aaa' =~ /((?<=a)a)/g;


3 commentaires

Si vous ne savez pas pourquoi cela s'appelle «Opérateur Goatse», do pas Essayez de déterminer ce que cela signifie. Certaines choses que vous feriez mieux de ne pas savoir.


"Certaines choses ne peuvent pas être invisibles"


La peur vient du nom;). Je ne l'ai pas posté comme une réponse, parce que je ne pensais pas à l'idée de Lookahead / Lookbehind, qui est la vraie réponse. L'idée d'utiliser une «lookedehind» avec l'opérateur de goatiser rend une certaine sorte de sens horrible.



8
votes
sub countnmstr
{
    my ($string, $substr) = @_;
    return scalar( $string =~ s/(?=\Q$substr\E)//g );
}

0 commentaires

3
votes

Si la vitesse est une question, l'approche code> indice code> suggéré par GhostDog74 (avec l'amélioration de CJM) est susceptible d'être considérablement plus rapide que les solutions REGEX.

use strict;
use warnings;

sub countnmstr_regex {
    my ($haystack, $needle) = @_;
    return scalar( () = $haystack =~ /(?=\Q$needle\E)/g );
}

sub countnmstr_index {
    my ($haystack, $needle) = @_;
    my $i = 0;
    my $tally = 0;
    while (1){
        $i = index($haystack, $needle, $i);
        last if $i == -1;
        $tally ++;
        $i ++;
    }
    return $tally;
}

use Benchmark qw(cmpthese);

my $size = 1;
my $h = 'aaa aaaaaa' x $size;
my $n = 'aa';

cmpthese( -2, {
    countnmstr_regex => sub { countnmstr_regex($h, $n) },
    countnmstr_index => sub { countnmstr_index($h, $n) },
} );

__END__

# Benchmarks run on Windows.
# Result using a small haystack ($size = 1).
                     Rate countnmstr_regex countnmstr_index
countnmstr_regex  93701/s               --             -66%
countnmstr_index 271893/s             190%               --

# Result using a large haystack ($size = 100).
                   Rate countnmstr_regex countnmstr_index
countnmstr_regex  929/s               --             -81%
countnmstr_index 4960/s             434%               --


4 commentaires

Cette référence a un problème dans la mesure où il ne concerne qu'un exemple très spécifique où l'indice est sûr de gagner. Maintenant, essayez-le avec un motif non littéral: l'indice perd parce qu'il ne peut même pas fonctionner. Faites très attention aux points de repère spéciaux.


@brian sûr, index ne fonctionne que avec du texte littéral - je pense que nous savions déjà cela. L'OP n'est pas clair s'il doit rechercher un texte littéral ou un modèle. Étant donné que la plupart des réponses se sont concentrées sur REGEX, il semble raisonnable d'illustrer une approche alternative qui a également un avantage rapide.


@brian: l'OP n'a jamais dit quoi que ce soit sur la correspondance d'une regex; Vous avez dit que lorsque vous avez édité le titre. Il a simplement utilisé une regex dans sa mise en œuvre initiale.


Assez juste et j'ai élargi le titre. Cependant, "jamais dit quoi que ce soit" est un peu fort puisqu'il l'a utilisé lui-même. :)