Pourquoi les grammaires ambiguës sont-elles mauvaises?
30
Je comprends que s'il existe 2 ou plusieurs arbres de dérivation gauche ou droit, alors la grammaire est ambiguë, mais je ne peux pas comprendre pourquoi elle est si mauvaise que tout le monde veut s'en débarrasser.
En effet, la forme non ambiguë est meilleure pour les utilisations pratiques, la forme non ambiguë utilise moins de règles de production pour construire un arbre plus petit en haut (donc un compilateur efficace prend moins de temps à analyser). La plupart des outils permettent de résoudre l'ambiguïté explicitement en dehors de la grammaire.
Grijesh Chauhan
3
"tout le monde veut s'en débarrasser". Eh bien, ce n'est tout simplement pas vrai. Dans les langues commercialement pertinentes, il est courant de voir une ambiguïté ajoutée à mesure que les langues évoluent. Par exemple, C ++ a intentionnellement ajouté l'ambiguïté std::vector<std::vector<int>>en 2011, qui nécessitait >>auparavant un espace entre les deux . L'idée clé est que ces langages ont beaucoup plus d'utilisateurs que de fournisseurs, donc la correction d'une gêne mineure pour les utilisateurs justifie beaucoup de travail par les implémenteurs.
MSalters
Réponses:
52
Considérez la grammaire suivante pour les expressions arithmétiques:
X→ X+ X∣ X- X∣ X∗ X∣ X/ X∣ var ∣ const
Considérez l'expression suivante:
a - b - c
Quelle est sa valeur? Voici deux arbres d'analyse possibles:
Selon celui de gauche, nous devrions interpréter a - b - c comme ( a - b ) - c , qui est l'interprétation habituelle. Selon celui de droite, nous devrions l'interpréter comme a - ( b - c ) = a - b + c , ce qui n'est probablement pas ce qui était prévu.
Lors de la compilation d'un programme, nous voulons que l'interprétation de la syntaxe soit sans ambiguïté. La façon la plus simple de faire respecter cela est d'utiliser une grammaire sans ambiguïté. Si la grammaire est ambiguë, nous pouvons fournir des règles de départage, comme la priorité des opérateurs et l'associativité. Ces règles peuvent être exprimées de manière équivalente en rendant la grammaire non ambiguë d'une manière particulière.
@HIRAKMONDAL Le fait que la syntaxe soit ambiguë n'est pas un vrai problème. le problème est que les deux arbres d'analyse différents ont un comportement différent. Si votre langue a une grammaire ambiguë mais que tous les arbres d'analyse d'une expression sont sémantiquement équivalents, ce ne serait pas un problème (par exemple, prenez l'exemple de Yuval et considérez le cas où votre seul opérateur +).
Bakuriu
14
@Bakuriu Ce que vous avez dit est vrai, mais "sémantiquement équivalent" est un défi de taille. Par exemple, l'arithmétique à virgule flottante n'est en fait pas associative (donc les deux arbres "+" ne seraient pas équivalents). De plus, même si la réponse est venue de la même manière, l'ordre d'évaluation non défini est très important dans les langues où les expressions peuvent avoir des effets secondaires. Donc, ce que vous avez dit est techniquement vrai, mais dans la pratique, il serait très inhabituel que l'ambiguïté d'une grammaire n'ait aucune répercussion sur l'utilisation de cette grammaire.
Richard Rast
De nos jours, certaines langues vérifient le dépassement d'entier dans les ajouts, donc même a + b + c pour les entiers dépend de l'ordre d'évaluation.
gnasher729
3
Pire encore, dans certains cas, la grammaire ne fournit aucun moyen d'atteindre le sens alternatif. Je l'ai vu dans les langages de requête, où le choix de la grammaire d'échappement (par exemple le double du caractère spécial pour l'échapper) rend certaines requêtes impossibles à exprimer.
Arrêtez de nuire à Monica le
12
Contrairement aux autres réponses existantes [ 1 , 2 ], il existe en effet un champ d'application, où les grammaires ambiguës sont utiles . Dans le domaine du traitement du langage naturel (NLP), lorsque vous voulez analyser le langage naturel (NL) avec des grammaires formelles, vous avez le problème que NL est intrinsèquement ambigu à différents niveaux [adapté de Koh18, ch. 6.4]:
Ambuigité syntaxique:
Peter a poursuivi l'homme dans la voiture de sport rouge
Peter ou l'homme était-il dans la voiture de sport rouge?
Ambuigité sémantique:
Peter est allé à la banque
Une banque pour s'asseoir ou une banque pour retirer de l'argent?
Ambuigité pragmatique:
Deux hommes portaient deux sacs
Portaient-ils les sacs ensemble ou chaque homme portait-il deux sacs?
Différentes approches pour la PNL traitent différemment le traitement en général et en particulier ces ambiguïtés. Par exemple, votre pipeline peut ressembler à ceci:
Analyser NL avec une grammaire ambiguë
Pour chaque AST résultant: exécuter la génération de modèle pour générer des significations sémantiques ambiguës et exclure les ambiguïtés syntaxiques impossibles de l'étape 1
Pour chaque modèle résultant: enregistrez-le dans votre cache.
Vous faites ce pipeline pour chaque phrase. Plus il y a de texte, par exemple, dans le même livre que vous traitez, plus vous pouvez exclure les modèles superflus impossibles, qui ont survécu jusqu'à l'étape 3, des phrases précédentes.
Contrairement au langage de programmation, nous pouvons abandonner l'exigence que chaque phrase NL ait une sémantique précise. Au lieu de cela, nous pouvons simplement tenir en compte plusieurs modèles sémantiques possibles tout au long de l'analyse de textes plus grands. De temps en temps, des informations ultérieures nous aident à écarter les ambiguïtés précédentes.
Si vous voulez vous salir les mains avec des analyseurs capables de produire plusieurs dérivations pour une grammaire ambiguë, jetez un œil au framework grammatical . De plus, [Koh18, ch. 5] a une introduction montrant quelque chose de similaire à mon pipeline ci-dessus. Notez cependant que puisque [Koh18] sont des notes de cours, les notes pourraient ne pas être faciles à comprendre par elles-mêmes sans les cours.
Merci beaucoup .. J'ai eu le même doute et vous l'
avez dissipé
1
Sans parler des problèmes avec Buffalo buffalo buffalo Buffalo buffalo buffalo ... pour un nombre adéquat de buffles
Hagen von Eitzen
Vous écrivez «en contraste», mais j'appellerais cela de l'autre côté de la médaille d'après ce que j'ai répondu. Analyser les langues naturelles avec leurs grammaires ambiguës est si difficile que les analyseurs traditionnels ne peuvent pas le faire!
Davislor
1
@ComFreek Je devrais être plus précis ici. Un bref aperçu de GF (merci pour le lien!) Montre qu'il lit des grammaires sans contexte avec trois extensions (comme autoriser la reduplication) et renvoie une liste de toutes les dérivations possibles. Les algorithmes pour le faire existent depuis les années 50. Cependant, être capable de gérer des CFG entièrement généraux signifie que votre pire cas d'exécution explose et, dans la pratique, même lorsqu'ils utilisent un analyseur général tel que GLL, les ingénieurs logiciels essaient d'utiliser un sous-ensemble de CFG, tels que les grammaires LL, qui peuvent être analysé plus efficacement.
Davislor
1
@ComFreek Ce n'est donc pas que les ordinateurs ne peuvent pas gérer CFG (bien que les langues naturelles ne soient pas vraiment sans contexte et que la traduction automatique réellement utile utilise des techniques complètement différentes). C'est que, si vous avez besoin que votre analyseur gère l'ambiguïté, cela exclut certains raccourcis qui l'auraient rendu plus efficace.
Davislor
10
Même s'il existe un moyen bien défini de gérer l'ambiguïté (les expressions ambiguës sont des erreurs de syntaxe, par exemple), ces grammaires posent toujours problème. Dès que vous introduisez une ambiguïté dans une grammaire, un analyseur ne peut plus être sûr que la première correspondance qu'il obtient est définitive. Il doit continuer à essayer toutes les autres façons d'analyser une déclaration, pour écarter toute ambiguïté. Vous n'avez pas non plus affaire à quelque chose de simple comme un langage LL (1), vous ne pouvez donc pas utiliser un analyseur simple, petit et rapide. Votre grammaire comporte des symboles qui peuvent être lus de plusieurs façons, vous devez donc être prêt à faire beaucoup de retours en arrière.
Dans certains domaines restreints, vous pourriez être en mesure de prouver que toutes les manières possibles d'analyser une expression sont équivalentes (par exemple, car elles représentent une opération associative). (a + b) + c = a + (b + c).
C'est un bon exemple montrant que même une grammaire non ambiguë (comme en Java, C, C ++, ...) permet des ambiguïtés apparentes (!) D'un point de vue humain. Même si nous sommes bien formellement et computationnellement bien, nous avons maintenant plus d'un problème de développement sans UX / bug.
ComFreek
5
Prenez l'analyse la plus vexante en C ++ par exemple:
bar foo(foobar());
S'agit-il d'une déclaration foode fonction de type bar(foobar())(le paramètre est un pointeur de fonction renvoyant a foobar), ou d'une déclaration foode variable de type intet initialisée avec une valeur par défaut initialisée foobar?
Ceci est différencié dans les compilateurs en supposant le premier sauf si l'expression à l'intérieur de la liste de paramètres ne peut pas être interprétée comme un type.
lorsque vous obtenez une expression si ambiguë, le compilateur a 2 options
supposons que l'expression est une dérivation particulière et ajoutez un peu d'ambiguïté à la grammaire pour permettre à l'autre dérivation d'être exprimée.
erreur et exiger la désambiguïsation de toute façon
Le premier peut tomber naturellement, le second nécessite que le programmeur du compilateur connaisse l'ambiguïté.
Si cette ambiguïté reste non détectée, il est possible que 2 compilateurs différents utilisent par défaut des dérivations différentes pour cette expression ambiguë. Menant à un code non portable pour des raisons non évidentes. Ce qui amène les gens à supposer que c'est un bogue dans l'un des compilateurs alors qu'il s'agit en fait d'un défaut dans la spécification du langage.
Je pense que la question contient une hypothèse qui n'est au mieux que limite.
Dans la vraie vie, il est assez courant de simplement vivre avec des grammaires ambiguës, tant qu'elles ne sont pas (pour ainsi dire) trop ambiguës.
Par exemple, si vous examinez les grammaires compilées avec yacc (ou similaire, comme le bison ou le byacc), vous constaterez que plusieurs produisent des avertissements sur les "conflits de décalage / réduction N" lorsque vous les compilez. Quand yacc rencontre un conflit de décalage / réduction, cela signale une ambiguïté dans la grammaire.
Un conflit de changement / réduction, cependant, est généralement un problème assez mineur. Le générateur d'analyseur résoudra le conflit en faveur du «décalage» plutôt que de la réduction. La grammaire est parfaitement bien si c'est ce que vous voulez (et cela semble fonctionner parfaitement bien dans la pratique).
Un conflit de décalage / réduction survient généralement dans un cas de cet ordre général (en utilisant des majuscules pour les non-terminaux et des minuscules pour les terminaux):
A -> B | c
B -> a | c
Lorsque nous rencontrons un c, il y a une ambiguïté: devons-nous analyser le cdirectement comme un A, ou devons-nous l'analyser comme un B, qui à son tour est un A? Dans un cas comme celui-ci, yacc et autres choisiront la route la plus simple / la plus courte, et analyseront cdirectement la route en tant que A, plutôt que de suivre la route c-> B-> A. Cela peut être faux, mais si c'est le cas, cela signifie probablement que vous avez une erreur très simple dans votre grammaire, et vous ne devriez pas du tout autoriser l' coption comme possibilité A.
Maintenant, en revanche, nous pourrions avoir quelque chose de plus comme ceci:
A -> B | C
B -> a | c
C -> b | c
Maintenant, lorsque nous rencontrons un, cnous avons un conflit entre le fait de traiter le ccomme un Bou un C. Il y a beaucoup moins de chances qu'une stratégie de résolution automatique des conflits choisisse ce que nous voulons vraiment. Ni l'un ni l'autre n'est un "changement" - les deux sont des "réductions", c'est donc un "réduire / réduire le conflit" (que ceux qui sont habitués au yacc et autres reconnaissent généralement comme un problème beaucoup plus important qu'un conflit de décalage / réduction).
Donc, même si je ne suis pas sûr, j'irais jusqu'à dire que quiconque accueille vraiment l' ambiguïté dans sa grammaire, dans au moins certains cas, c'est suffisamment mineur pour que personne ne s'en soucie vraiment. Dans l'abstrait, ils pourraient aimer l'idée de lever toute ambiguïté - mais pas assez pour toujours le faire. Par exemple, une petite grammaire simple qui contient une ambiguïté mineure peut être préférable à une grammaire plus grande et plus complexe qui élimine l'ambiguïté (surtout lorsque vous entrez dans le domaine pratique de la génération effective d'un analyseur syntaxique à partir de la grammaire et que la grammaire produit un analyseur qui ne fonctionnera pas sur votre machine cible).
std::vector<std::vector<int>>
en 2011, qui nécessitait>>
auparavant un espace entre les deux . L'idée clé est que ces langages ont beaucoup plus d'utilisateurs que de fournisseurs, donc la correction d'une gêne mineure pour les utilisateurs justifie beaucoup de travail par les implémenteurs.Réponses:
Considérez la grammaire suivante pour les expressions arithmétiques:X→ X+ X∣ X- X∣ X∗ X∣ X/ X∣ var ∣ const
Considérez l'expression suivante:
a - b - c
Quelle est sa valeur? Voici deux arbres d'analyse possibles:
Selon celui de gauche, nous devrions interprétera - b - c comme ( a - b ) - c , qui est l'interprétation habituelle. Selon celui de droite, nous devrions l'interpréter comme a - ( b - c ) = a - b + c , ce qui n'est probablement pas ce qui était prévu.
Lors de la compilation d'un programme, nous voulons que l'interprétation de la syntaxe soit sans ambiguïté. La façon la plus simple de faire respecter cela est d'utiliser une grammaire sans ambiguïté. Si la grammaire est ambiguë, nous pouvons fournir des règles de départage, comme la priorité des opérateurs et l'associativité. Ces règles peuvent être exprimées de manière équivalente en rendant la grammaire non ambiguë d'une manière particulière.
Analyser les arbres générés à l'aide du générateur d'arbres syntaxiques .
la source
+
).Contrairement aux autres réponses existantes [ 1 , 2 ], il existe en effet un champ d'application, où les grammaires ambiguës sont utiles . Dans le domaine du traitement du langage naturel (NLP), lorsque vous voulez analyser le langage naturel (NL) avec des grammaires formelles, vous avez le problème que NL est intrinsèquement ambigu à différents niveaux [adapté de Koh18, ch. 6.4]:
Différentes approches pour la PNL traitent différemment le traitement en général et en particulier ces ambiguïtés. Par exemple, votre pipeline peut ressembler à ceci:
Vous faites ce pipeline pour chaque phrase. Plus il y a de texte, par exemple, dans le même livre que vous traitez, plus vous pouvez exclure les modèles superflus impossibles, qui ont survécu jusqu'à l'étape 3, des phrases précédentes.
Contrairement au langage de programmation, nous pouvons abandonner l'exigence que chaque phrase NL ait une sémantique précise. Au lieu de cela, nous pouvons simplement tenir en compte plusieurs modèles sémantiques possibles tout au long de l'analyse de textes plus grands. De temps en temps, des informations ultérieures nous aident à écarter les ambiguïtés précédentes.
Si vous voulez vous salir les mains avec des analyseurs capables de produire plusieurs dérivations pour une grammaire ambiguë, jetez un œil au framework grammatical . De plus, [Koh18, ch. 5] a une introduction montrant quelque chose de similaire à mon pipeline ci-dessus. Notez cependant que puisque [Koh18] sont des notes de cours, les notes pourraient ne pas être faciles à comprendre par elles-mêmes sans les cours.
Les références
[Koh18]: Michael Kohlhase. "Logic-Based Natural Language Processing. Winter Semester 2018/19. Lecture Notes." URL: https://kwarc.info/teaching/LBS/notes.pdf . URL de la description du cours: https://kwarc.info/courses/lbs/ (en allemand)
[Koh18, ch. 5]: Voir le chapitre 5, "Implémentation de fragments: cadres grammaticaux et logiques", dans [Koh18]
[Koh18, ch. 6.4] Voir le chapitre 6.4, "Le rôle de calcul des ambiguïtés", dans [Koh18]
la source
Même s'il existe un moyen bien défini de gérer l'ambiguïté (les expressions ambiguës sont des erreurs de syntaxe, par exemple), ces grammaires posent toujours problème. Dès que vous introduisez une ambiguïté dans une grammaire, un analyseur ne peut plus être sûr que la première correspondance qu'il obtient est définitive. Il doit continuer à essayer toutes les autres façons d'analyser une déclaration, pour écarter toute ambiguïté. Vous n'avez pas non plus affaire à quelque chose de simple comme un langage LL (1), vous ne pouvez donc pas utiliser un analyseur simple, petit et rapide. Votre grammaire comporte des symboles qui peuvent être lus de plusieurs façons, vous devez donc être prêt à faire beaucoup de retours en arrière.
Dans certains domaines restreints, vous pourriez être en mesure de prouver que toutes les manières possibles d'analyser une expression sont équivalentes (par exemple, car elles représentent une opération associative). (a + b) + c = a + (b + c).
la source
Est-ce que
IF a THEN IF b THEN x ELSE y
moyenneou
? AKA le problème de balancer autrement .
la source
Prenez l'analyse la plus vexante en C ++ par exemple:
S'agit-il d'une déclaration
foo
de fonction de typebar(foobar())
(le paramètre est un pointeur de fonction renvoyant afoobar
), ou d'une déclarationfoo
de variable de typeint
et initialisée avec une valeur par défaut initialiséefoobar
?Ceci est différencié dans les compilateurs en supposant le premier sauf si l'expression à l'intérieur de la liste de paramètres ne peut pas être interprétée comme un type.
lorsque vous obtenez une expression si ambiguë, le compilateur a 2 options
supposons que l'expression est une dérivation particulière et ajoutez un peu d'ambiguïté à la grammaire pour permettre à l'autre dérivation d'être exprimée.
erreur et exiger la désambiguïsation de toute façon
Le premier peut tomber naturellement, le second nécessite que le programmeur du compilateur connaisse l'ambiguïté.
Si cette ambiguïté reste non détectée, il est possible que 2 compilateurs différents utilisent par défaut des dérivations différentes pour cette expression ambiguë. Menant à un code non portable pour des raisons non évidentes. Ce qui amène les gens à supposer que c'est un bogue dans l'un des compilateurs alors qu'il s'agit en fait d'un défaut dans la spécification du langage.
la source
Je pense que la question contient une hypothèse qui n'est au mieux que limite.
Dans la vraie vie, il est assez courant de simplement vivre avec des grammaires ambiguës, tant qu'elles ne sont pas (pour ainsi dire) trop ambiguës.
Par exemple, si vous examinez les grammaires compilées avec yacc (ou similaire, comme le bison ou le byacc), vous constaterez que plusieurs produisent des avertissements sur les "conflits de décalage / réduction N" lorsque vous les compilez. Quand yacc rencontre un conflit de décalage / réduction, cela signale une ambiguïté dans la grammaire.
Un conflit de changement / réduction, cependant, est généralement un problème assez mineur. Le générateur d'analyseur résoudra le conflit en faveur du «décalage» plutôt que de la réduction. La grammaire est parfaitement bien si c'est ce que vous voulez (et cela semble fonctionner parfaitement bien dans la pratique).
Un conflit de décalage / réduction survient généralement dans un cas de cet ordre général (en utilisant des majuscules pour les non-terminaux et des minuscules pour les terminaux):
Lorsque nous rencontrons un
c
, il y a une ambiguïté: devons-nous analyser lec
directement comme unA
, ou devons-nous l'analyser comme unB
, qui à son tour est unA
? Dans un cas comme celui-ci, yacc et autres choisiront la route la plus simple / la plus courte, et analyserontc
directement la route en tant queA
, plutôt que de suivre la routec
->B
->A
. Cela peut être faux, mais si c'est le cas, cela signifie probablement que vous avez une erreur très simple dans votre grammaire, et vous ne devriez pas du tout autoriser l'c
option comme possibilitéA
.Maintenant, en revanche, nous pourrions avoir quelque chose de plus comme ceci:
Maintenant, lorsque nous rencontrons un,
c
nous avons un conflit entre le fait de traiter lec
comme unB
ou unC
. Il y a beaucoup moins de chances qu'une stratégie de résolution automatique des conflits choisisse ce que nous voulons vraiment. Ni l'un ni l'autre n'est un "changement" - les deux sont des "réductions", c'est donc un "réduire / réduire le conflit" (que ceux qui sont habitués au yacc et autres reconnaissent généralement comme un problème beaucoup plus important qu'un conflit de décalage / réduction).Donc, même si je ne suis pas sûr, j'irais jusqu'à dire que quiconque accueille vraiment l' ambiguïté dans sa grammaire, dans au moins certains cas, c'est suffisamment mineur pour que personne ne s'en soucie vraiment. Dans l'abstrait, ils pourraient aimer l'idée de lever toute ambiguïté - mais pas assez pour toujours le faire. Par exemple, une petite grammaire simple qui contient une ambiguïté mineure peut être préférable à une grammaire plus grande et plus complexe qui élimine l'ambiguïté (surtout lorsque vous entrez dans le domaine pratique de la génération effective d'un analyseur syntaxique à partir de la grammaire et que la grammaire produit un analyseur qui ne fonctionnera pas sur votre machine cible).
la source