31 votes

L'étoile de Kleene : pourquoi $_ = "a" ; s/a*/e/g produit : ee

a* signifie zéro ou plusieurs instances de : un droit ?

alors pourquoi $_ = "a"; s/a*/e/g produire : ee

Réponse possible : il s'agit de remplacer la chaîne : "a" par : "e" et il remplace la chaîne vide : "" par : "e" également. Ou bien il remplace la simple absence d'une lettre : a par une lettre : e ou bien il remplace "zéro occurrence" de : a par un : e.

Ok alors, mais :

$_ = "b"; s/a*/e/g produit : ebe

Il semble remplacer la chaîne vide à gauche de : b et également la chaîne vide à droite de : b

OK. Mais alors pourquoi ne le fait-il pas pour : " a " ? Pourquoi ne remplace-t-il pas la chaîne vide à gauche de : a et également la chaîne vide à droite de : a et également la lettre : un lui-même à obtenir : eee ?

Il y a autant d'occurrences zéro de : a à gauche qu'à droite !

27voto

DVK Points 63282

Votre analyse de la raison pour laquelle les résultats sont "ee" et "ebe" sont tout à fait exactes.

Le modificateur "/g" fait en sorte que l'expression rationnelle ne corresponde qu'une seule fois, puis tente à nouveau de correspondre à partir de l'endroit où la dernière correspondance s'est arrêtée.

La raison de l'écart (il ne remplace pas la chaîne vide à gauche de "a" ) est que c'est parce que "*" est gourmand - il correspond au PLUS grand nombre de caractères possible. De perldoc perlre :

Par défaut, un sous-modèle quantifié est "gourmand", c'est-à-dire qu'il correspondra autant de fois que possible (compte tenu d'un emplacement de départ particulier) tout en permettant au reste du modèle de correspondre.

Donc, il correspond à zéro "a", et voit s'il peut en correspondre plus. Puisqu'il y a plus de "a" dans la chaîne, il en trouve un de plus. Essayez d'en trouver d'autres. Aucun ? C'est fait. Nous faisons donc correspondre le premier "a".

Ensuite, "/g" nous oblige à réessayer de correspondre ( en reprenant là où nous nous sommes arrêtés après la fin du dernier match ), qui correspond maintenant à une chaîne vide (zéro "a").

19voto

zostay Points 2879

En utilisant l'excellent ouvrage de Damian Conway Regexp::Debugger J'ai essayé ça :

perl -MRegexp::Debugger -E '$_ = "a"; s/a*/e/g; say'

Et j'ai obtenu ce résultat, au cas où cela rendrait les choses plus claires, en mode d'enregistrement des événements. Le premier passage de la correspondance à travers le remplacement produit cet ensemble d'événements :

a               | a*              |   Starting regex match
a               | a*              |     Trying a literal character zero-or-more times (as many as possible)
                | a*              |     Matched
                |                 |   Regex matched in 3 steps

Cela montre que le "a" est trouvé la première fois, et qu'il est remplacé par "e".

Après avoir terminé le match la première fois, le débogueur me permet de lancer un deuxième match à partir du même programme :

                | <~~             |   Back-tracking in regex
                | a*              |   Back-tracked and restarting regex match
                | a*              |     Trying a literal character zero-or-more times (as many as possible)
                | a*              |     Matched
                |                 |   Regex matched in 3 steps

Cela montre que le "" après le "a" original (maintenant "e") correspond à la deuxième fois et est remplacé par "e".

Malheureusement, soit je ne sais pas comment lire la sortie, soit Regexp::Debugger s'embrouille à ce moment-là, mais il répète encore une fois, mais ne fait pas de remplacement.

                | <~~             |   Back-tracking in regex
                | a*              |   Back-tracked and restarting regex match
                | a*              |     Trying a literal character zero-or-more times (as many as possible)
                | a*              |     Matched
                |                 |   Regex matched in 3 steps

Quoi qu'il en soit, soit Perl a trouvé une troisième correspondance et a décidé, pour une raison ou une autre, de ne pas effectuer de remplacement cette fois-ci, soit Regexp::Debugger, soit je suis simplement confus.

Edit : J'ai résolu ma confusion en revoyant perldoc perlre :

" Les boucles de niveau supérieur préservent un état supplémentaire entre les itérations : si la dernière correspondance était de longueur zéro. Pour rompre la boucle, il est interdit que le match suivant après un match de longueur nulle ait une longueur de zéro. Cette interdiction interagit avec le backtracking (voir "Backtracking"), et donc le deuxième meilleur match est choisi si le meilleur match est de longueur zéro."

8voto

hobbs Points 71946

D'abord, comme les gens l'ont dit, a* est gourmand ; il ne correspondra pas à la chaîne vide s'il peut correspondre à "a" à la place. Deuxièmement, a /g match correspondra autant de fois que possible, mais il ne fera pas de correspondance de longueur nulle deux fois de suite. dans la même position car cela signifie que le motif ne progresse pas. Le motif est obligé de faire une autre correspondance de longueur non nulle s'il le peut, sinon il échoue.

En cours d'exécution s/a*/e/g sur "a", premièrement a* correspond à "a" à la position 0 (et avance à la position 1), donc le "a" est remplacé par "e". Ensuite, a* correspond à la chaîne vide en position 1 (et n'avance pas), donc "" est remplacé par "e". Maintenant nous sommes toujours à la position 1, et a* n'a pas le droit de correspondre à nouveau à la chaîne vide, et ne peut pas correspondre à quelque chose de plus long, donc le motif échoue et perl essaie de passer au caractère suivant de la chaîne. Mais nous avons atteint la fin de la chaîne de caractères, donc la sortie est "ee".

En cours d'exécution s/a*/e/g sur "b", premièrement a* correspond à la chaîne vide en position 0 (et n'avance pas), remplaçant "" par "e". Ensuite, une autre correspondance à la position 0 est interdite, donc le motif avance à la position 1 (en passant sur "b" qui n'est pas remplacé). Ensuite, a* correspond à la chaîne vide en position 1, et la remplace par "e" ; et encore une fois, il est interdit de correspondre deux fois à la même position et perl ne peut pas avancer au-delà de la fin de la chaîne, donc le résultat est "ebe".

Enfin, imaginez que vous exécutez s/a*/e/g sur "ab". a* correspond à "aa" en position 0, le remplace par "e", et avance à la position 2 ; a* correspond à la chaîne vide en position 2, la remplace par "e" et n'avance pas ; a* ne peut pas faire de correspondance non vide et échoue ; "b" est parcouru ; a* correspond à la chaîne vide en position 3, la remplace par "e" et n'avance pas ; fin de la chaîne. Le résultat est donc "eebe", comme le confirmera perl.

2voto

ikegami Points 133140

Vous pensez que c'est incohérent parce que vous pensez qu'il remplace "la chaîne vide en position 0" alors qu'il remplace en fait "les séquences de 'a' en position 0". Vous ne devriez pas être surpris que la séquence soit plus longue lorsque l'entrée est a par rapport à b .

$_ = "a"; s/a*/e/g :

  1. Essai à la position 0 : Correspondance d'un caractère à la position 0. Pos = 1.
  2. Essai à la position 1 : Correspondance avec le 0 char à la position 1. Pos = 1.
  3. Essayez à la position 1 : Correspondance avec le caractère 0 en position 1. Oups, c'est déjà fait, donc échec à cette position. Position = 2.

$_ = "b"; s/a*/e/g :

  1. Essai à la position 0 : Correspondance 0 char à la position 0. Pos = 0.
  2. Essayez à la position 0 : Correspondance avec le caractère 0 en position 0. Oups, c'est déjà fait, donc échec à cette position. Pos = 1.
  3. Essai à la position 1 : Correspondance avec le 0 char à la position 1. Pos = 1.
  4. Essayez à la position 1 : Correspondance avec le caractère 0 en position 1. Oups, j'ai déjà fait ça, donc échec à ce poste. Position = 2.

Si vous voulez faire correspondre une chaîne vide à la position 0, vous devrez lui demander de le faire.

>perl -E"say 'a' =~ s/^|a*/e/gr;"
eee

>perl -E"say 'b' =~ s/^|a*/e/gr;"
ebe

1voto

Jonathan Leffler Points 299946

Très curieux. En utilisant Perl 5.12.1 sur RHEL 5, la sortie est bien celle indiquée :

$ perl -e '$_ = "a"; s/a*/e/g; print "$_\n";'
ee
$

La meilleure supposition (raison) que je peux faire est que le a* correspond d'abord à la a ce qui donne le premier e et correspond ensuite à la chaîne vide après le a pour le second e . Essayons quelques variantes :

$ perl -e '$_ = "a"; s/^a*/e/g; print "$_\n";'
e
$ perl -e '$_ = "a"; s/a*$/e/g; print "$_\n";'
ee
$ perl -e '$_ = "a"; s/a+/e/g; print "$_\n";' 
e
$

La première et la troisième de ces variations produisent les réponses que j'attendais. La deuxième me laisse encore perplexe.

$ perl -e '$_ = "a\n"; s/a*/e/g; print "$_\n";'
ee
e
$

Hmmm...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X