web-dev-qa-db-fra.com

Que signifie "sans contexte" dans le terme "grammaire sans contexte"?

Compte tenu de la quantité de matériel qui tente d'expliquer ce qu'est une grammaire sans contexte (CFG), j'ai trouvé surprenant que très peu (dans mon échantillon, moins de 1 sur 20) donnent une explication sur la raison pour laquelle de telles grammaires sont appelées "contexte". gratuit". Et, à mon avis, personne n'y parvient.

Ma question est la suivante: pourquoi les grammaires sans contexte sont-elles appelées sans contexte? Qu'est-ce que "le contexte"? J'ai eu l'intuition que le contexte pourrait être d'autres constructions de langage entourant la construction actuellement analysée, mais cela ne semble pas être le cas. Quelqu'un pourrait-il fournir une explication précise?

56
rick

Cela signifie que tous ses règles de production ont un seul non terminal sur leur gauche.

Par exemple, cette grammaire qui reconnaît les chaînes de parenthèses correspondantes ("()", "() ()", "(()) ()", ...) est sans contexte:

S → SS
S → (S)
S → ()

Le côté gauche de chaque règle se compose d'un seul non-terminal (dans ce cas, c'est toujours S, mais il pourrait y en avoir plus.)

Considérons maintenant cette autre grammaire qui reconnaît les chaînes de la forme {a ^ n b ^ n c ^ n: n> = 1} (par exemple "abc", "aabbcc", "aaabbbccc"):

S  → abc
S  → aSBc
cB → WB
WB → WX
WX → BX
BX → Bc
bB → bb

Si le non-terminal B est précédé du caractère terminal/littéral c, vous réécrivez ce terme dans WB mais s'il est précédé de b, vous passez à bb à la place. C'est probablement ce à quoi fait allusion la sensibilité au contexte des grammaires contextuelles.

Un langage sans contexte peut être reconnu par automate déroulant . Alors qu'une machine à états finis n'utilise aucun stockage auxiliaire, c'est-à-dire que sa décision est basée uniquement sur son état et son entrée actuels, un automate Push-down a également une pile à sa disposition et peut jeter un œil au sommet de la pile pour prendre des décisions.

Pour voir cela en action, vous pouvez analyser les parenthèses imbriquées en vous déplaçant de gauche à droite et en poussant une parenthèse gauche sur une pile chaque fois que vous en rencontrez une, et en sautant chaque fois que vous rencontrez une parenthèse droite. Si vous n'essayez jamais de sauter à partir d'une pile vide et que la pile est vide à la fin de la chaîne, la chaîne est valide.

Pour un langage contextuel, un PDA ne suffit pas. Vous aurez besoin d'un automate à délimitation linéaire qui est comme une machine de Turing dont la bande n'est pas illimité (bien que la quantité de bande disponible soit proportionnelle à l'entrée). Notez que cela décrit assez bien les ordinateurs - nous aimons les considérer comme des machines de Turing mais dans le monde réel, vous ne pouvez pas en saisir arbitrairement plus RAM mid-program. S'il n'est pas évident pour vous comment un LBA est plus puissant qu'un PDA, un LBA peut émuler un PDA en utilisant une partie de sa bande comme pile, mais il peut également choisir d'utiliser sa bande de différentes manières.

(Si vous vous demandez ce qu'une machine à états finis peut reconnaître, la réponse est des expressions régulières. Mais pas les expressions rationnelles sur les stéroïdes avec des groupes de capture et le look-behind/look-ahead que vous voyez dans les langages de programme; je veux dire ceux que vous pouvez construire avec des opérateurs comme [abc], |, *, +, et ?. Vous pouvez voir que abbbz correspond à regex ab*z simplement en conservant votre position actuelle dans la chaîne et l'expression régulière, aucune pile requise.)

60
Doval

Les autres réponses sont assez longues, même si elles sont précises et correctes. C'est la version courte.

Si vous avez une chaîne de caractères (terminaux et non terminaux) et que vous souhaitez remplacer un non terminal dans la chaîne, une grammaire sans contexte vous permet de le faire indépendamment des caractères entourant le non terminal.

Tenez compte des règles suivantes (les minuscules sont des terminaux, les majuscules ne sont pas des terminaux):

A -> a
AB -> a

Dans la première règle, vous pouvez remplacer un Aindépendamment de ce qui apparaît autour de lui (contexte). Dans la deuxième règle, vous ne pouvez pas remplacer A sauf s'il est suivi de B. Alors que les deux terminaux ne seront pas remplacés dans ce cas, le point important est que les terminaux non entourant la matière A. On ne peut pas remplacer BA par a, ou B par a: uniquement un A suivi d'un B car l'ordre, le contexte des non terminaux est important. Cela signifie que le contexte d'un non-terminal est important dans la deuxième règle, ce qui le rend sensible au contexte, tandis que la première règle est sans contexte.

20
user22815

Pour mieux comprendre la distinction et la terminologie, c'est une bonne idée de contraster un langage sans contexte comme un n b n  avec un contexte comme un n b n c n . (Notation: a, b et c sont des littéraux ici et l'exposant n signifie répéter le littéral n fois, n > 0, disons.) Par exemple, aabbc ou aabbbcc n'est pas dans cette dernière langue, tandis que aabbcc l'est.

Un accepteur pour la langue sans contexte a n b n  peut contracter une paire de a et b indépendamment de ce qui l'entoure (c'est-à-dire quel que soit le contexte dans lequel ab apparaît) et il fonctionnera correctement, n'acceptant que des chaînes dans la langue et rejetant toute autre chose, c'est-à-dire que la grammaire est S -> aSb | ab. Notez qu'il n'y a aucun terminal sur le côté gauche de la production (s) . (Il existe deux règles de production, mais nous les écrivons simplement de manière compacte.) L'accepteur peut essentiellement prendre une décision locale et sans contexte.

En revanche, vous ne pouvez pas faire quelque chose comme ça pour le langage contextuel un n b n c n , car pour ce dernier, vous devez vous rappeler en quelque sorte le contexte dans lequel vous vous trouviez, c'est-à-dire combien de contractions d'ab vous faites pour les faire correspondre avec des contractions de bc. Une grammaire pour cette dernière langue est

S -> abc | aBSc
Ba -> aB
Bb -> bb

Notez que vous avez des terminaux et des non-terminaux à gauche dans les deux dernières règles. Les terminaux de gauche sont le contexte dans lequel les non-terminaux peuvent être étendus.


Bootnote concernant la terminologie "contrat" ​​vs "étendre" etc.: bien que les grammaires formelles soient [formellement, hah] génératives, la façon dont elles sont réellement implémentées dans les analyseurs est en fait réductionniste, c'est-à-dire que vous contactez tout à un non-terminal, essentiellement appliquer les règles "à l'envers", c'est pourquoi même la première grammaire donnée ci-dessus n'est pas pratique dans un programme (cela vous donnerait le fameux conflit shift-réduire parce que vous ne pouvez pas décider quelle règle appliquer), mais les deux ci-dessus les grammaires suffisent pour illustrer la distinction entre sans contexte et sensible au contexte. La question de l'ambiguïté dans les grammaires hors contexte est assez compliquée, et ce n'est pas vraiment le sujet de cette question, donc je ne vais pas en dire plus ici, d'autant plus qu'il s'avère que Wikipedia a un article décent à ce sujet . En revanche, ses articles sur le contexte et en particulier celui sur le langage contextuel sont! @ # $ @! # $, Surtout si vous êtes nouveau sur le sujet ... Je suppose que c'est plus sur ma liste TODO.

7
Fizz

Les réponses ci-dessus donnent une assez bonne définition de ce que c'est. Voyons si je peux le dire avec mes propres mots, pour que vous ayez 23 explications au lieu de 20. Le but d'une grammaire, n'importe quelle grammaire, est de déterminer si une phrase particulière est une phrase dans la langue donnée. Cependant, ce que nous utilisons vraiment pour les grammaires et l'analyse, c'est de comprendre ce que signifie la phrase. C'est comme l'ancien schéma d'une phrase que vous avez peut-être faite ou non en classe d'anglais à l'école. Une phrase est composée d'une partie sujet et d'une partie prédicat, une partie sujet a un nom et peut-être quelques adjectifs, une partie prédicat a un verbe et peut-être un nom objet, avec quelques adjectifs supplémentaires, etc.

S'il y avait une grammaire pour l'anglais (et je ne pense pas qu'il y en ait, pas au sens de l'informatique), alors elle aurait des règles de la forme suivante, appelées productions.

Sentence -> SubjectPart PredicatePart
SubjectPart -> Adjective Noun

etc...

Vous pourriez alors écrire un programme et lui remettre n'importe quelle phrase, et le programme pourrait utiliser la grammaire pour comprendre quelle partie de la phrase chaque mot est, et quelle relation ils ont les uns avec les autres.

Si dans chaque production, il n'y a qu'une seule chose sur le côté gauche, cela signifie que chaque fois que vous voyez le côté droit dans la phrase, vous êtes autorisé à substituer dans le côté gauche. Par exemple, chaque fois que vous voyez un nom adjectif, vous pouvez dire "C'est une partie du sujet" sans prêter attention à quoi que ce soit en dehors de cette phrase.

Cependant, l'anglais (même la description simplifiée de l'anglais que j'ai donnée ci-dessus) est sensible au contexte. Le "nom adjectif" n'est pas toujours un SubjectPart, il peut s'agir d'un NounPhrase dans un PredicatePart. Ça dépend du contexte. Développons un peu notre grammaire pseudo-anglaise:

Sentence -> SubjectPart PredicatePart
SubjectPart -> Adjective Noun
PredicatePart -> VerbPhrase ObjectNounPhrase
VerbPhrase ObjectNounPhrase -> VerbPhrase Adjective Noun

Vous ne pouvez transformer un "nom adjectif" en ObjectNounPhrase que s'il vient juste après un VerbPhrase.

Fondamentalement, si vous avez une production et que vous pouvez l'appliquer à tout moment, peu importe ce qui l'entoure, elle est sans contexte.

Vous pouvez toujours savoir si une grammaire est sans contexte facilement. Vérifiez simplement s'il y a plus d'un symbole sur le côté gauche des flèches.

Toute langue peut être décrite par plusieurs grammaires. Si une grammaire pour une langue est sans contexte, la langue est sans contexte. Il peut être prouvé pour certaines langues qu'il n'y a pas de grammaire sans contexte possible. Je suppose qu'il pourrait y avoir une grammaire sans contexte pour le sous-ensemble pseudo-anglais simplifié que je décris ci-dessus.

Quant à savoir pourquoi cela importe, il nécessite un type de programme plus simple pour analyser une grammaire sans contexte. Comme indiqué dans les autres réponses, il ne nécessite pas la pleine puissance d'une machine de Turing pour analyser une grammaire sans contexte. Un analyseur d'anticipation LR (1) (qui est une sorte de machine à refouler) pour une grammaire sans contexte particulière peut analyser n'importe quelle phrase de cette grammaire dans le temps et l'espace linéairement à la longueur de la phrase. Si la phrase est dans la langue, l'analyseur va produire un arbre de structure identifiant ce que chaque symbole dans la phrase signifie (ou au moins quel rôle il joue dans la structure). Si la phrase n'est pas dans la grammaire, l'analyseur remarquera et s'arrêtera sur le premier symbole impossible à concilier avec la grammaire et les symboles précédents (sur la première "erreur").

Ce qui est encore mieux, c'est qu'il existe des programmes que vous pouvez donner une description d'une grammaire, et une liste d'instructions sur ce qu'il faut faire avec chaque partie (dans un sens, attacher un "sens" à chaque production) et le programme va écrire l'analyseur pour vous. Le programme analysera la phrase, trouvera la structure et exécutera vos instructions sur chaque partie de la structure. Ce type de programme est appelé générateur-analyseur ou compilateur-compilateur.

Ce type d'analyse du langage a été inventé pour l'analyse automatique du langage naturel (comme l'anglais), mais il s'avère que c'est le plus utile pour analyser les langages informatiques. Un concepteur de langage peut écrire une grammaire qui capture sa nouvelle langue, puis l'exécuter via le générateur d'analyseur pour obtenir un programme qui analyse sa langue et traduit, interprète, compile, exécute, etc. s'il le souhaite.

En fait, dans la plupart des cas, vous ne pouvez pas vraiment faire cela. Par exemple, les parenthèses équilibrées sont un langage sans contexte, mais un langage où il est nécessaire de déclarer toutes les variables avant de les utiliser est sensible au contexte. L'analyseur fait partie du compilateur, mais une logique supplémentaire est requise pour appliquer ces autres exigences. Ce que vous devez ensuite faire est d'écrire une grammaire qui capture autant de votre langue que possible, de l'exécuter via un générateur d'analyseur, puis d'écrire du code qui applique le reste des exigences (gestionnaire de table de symboles, etc.).

Nous n'utilisons généralement pas de grammaires contextuelles car elles sont beaucoup plus mal prises en charge. Je ne sais pas s'il existe un équivalent à un générateur d'analyseur LR (k) pour les langages contextuels. Oui, une machine de Turing (ou une machine liée linéaire) peut en analyser une, mais je ne sais pas s'il existe un algorithme général pour transformer une grammaire contextuelle en un programme pour une machine de Turing, en ce sens qu'un LR (1 ), le générateur crée des tables d'analyse pour une machine déroulante. Je suppose que les tables qui sous-tendent l'analyseur seraient exponentiellement plus grandes. Dans tous les cas, les étudiants CS (comme moi, dans la journée) apprennent généralement des grammaires sans contexte et des générateurs d'analyseurs LR (1) tels que YACC.

5
kwan3217