Que veut dire «sans contexte» dans le terme «grammaire sans contexte»?

55

Étant donné la quantité de matériel qui tente d'expliquer ce qu'est une grammaire sans contexte (CFG), j'ai trouvé étonnant que très peu (dans mon échantillon, moins d'un sur 20) expliquent pourquoi de telles grammaires sont appelées "contexte". libre". Et, à mon avis, aucun ne réussit à le faire.

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

meule
la source
4
Cherchez "l'analyse la plus frustrante" pour C ++ qui vous apprendra pourquoi le contexte-liberté est pratique
Ratchet Freak
6
Je pensais savoir ce qu’était une grammaire sans contexte jusqu’à ce que je lise quelques définitions de Google. Maintenant, j'aimerais avoir une esquisse et un blanc cassé ... peut-être que je vais simplement sortir ... +1 pour une bonne question. Dans l'attente de réponses intelligibles!
BrianH
Votre intuition est ce que je comprends, même si la définition formelle de "constructions d’autres langages entourant la construction actuellement analysée" est convenablement obscure. Mais je ne suis pas assez sûr pour poster cela comme réponse.
Telastyn
1
Voir wikipages sur la grammaire sans contexte et la hiérarchie de Chomsky . Dans la pratique, l' analyse du langage de programmation a un contexte, souvent traité "en dehors de" de l' analyse "sans contexte" (LR ou LL), par exemple, par une table de symboles, des attributs ou un environnement
Basile Starynkevitch
1
Ici, ont une référence xkcd: xkcd.com/1090
CaptainCodeman

Réponses:

60

Cela signifie que toutes 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 identiques ("()", "() ()", "(()) ()", ...) est sans contexte:

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

Le côté gauche de chaque règle consiste en 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 ^ nb ^ nc ^ 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 Best précédé du caractère terminal / littéral c, vous réécrivez ce terme en WBmais s'il est précédé de b, vous développez bbplutôt. C’est ce à quoi la sensibilité au contexte des grammaires sensibles au contexte fait allusion.

Un langage sans contexte peut être reconnu comme un automate à pile . Alors qu’une machine à états finis n’utilise pas de mémoire auxiliaire, c’est-à-dire que sa décision ne repose que sur son état et son entrée actuels, un automate à pile possède également une pile et peut regarder 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 plaçant 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 ne tentez jamais de sortir d'une pile vide et que celle-ci est vide à la fin de la chaîne, celle-ci est valide.

Pour un langage contextuel, un PDA ne suffit pas. Vous aurez besoin d'un automate à bornes linéaires qui ressemble à une machine de Turing dont la bande n'est pas illimitée (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 capturer arbitrairement plus de mémoire vive à mi-programme. S'il n'est pas évident pour vous de comprendre pourquoi un LBA est plus puissant qu'un PDA, il peut émuler un PDA en utilisant une partie de sa bande en tant que 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 une vision en arrière que vous voyez dans les langages de programme; je veux dire celles que vous pouvez construire. avec des opérateurs comme [abc], |, *, +et ?. Vous pouvez voir que les abbbzmatchs regex ab*zjuste en gardant votre position actuelle dans la chaîne et regex, pas la pile nécessaire.)

Doval
la source
14
Très belle explication. Bien que la bande d'une machine de Turing n'ait pas besoin d'être infinie, seulement illimitée. Il peut y avoir une fabrique de bandes à chaque extrémité qui, lorsque la machine cogne dessus, produit simplement plus de bande. De cette façon, à tout moment, il est fini.
Mike Dunlavey
2
@ MikeDunlavey Merci pour la clarification, corrigé.
Doval
10
Mais l'usine de bande aurait besoin de matériaux de fabrication de bande infinis, ou de matériaux de fabrication de bande infinis, ou ... [pile débordement]
flamingpenguin
8
@ Mehrdad: Vous pouvez simuler n'importe quel nombre de piles en utilisant deux piles: conservez toutes les piles empilées les unes sur les autres sur une pile et, lorsque vous avez besoin d'accéder à une pile plus éloignée, ouvrez les piles supérieures et déposez-les sur la deuxième pile. Cela prouve que n> 2 piles ne sont pas plus puissantes que 2 piles. Maintenant, si 2 piles sont plus puissantes que 1 pile, ça je ne le sais pas. Mon intuition dit non, mais cela pourrait dépendre de ce que sont exactement les primitives de la pile.
Jörg W Mittag
10
@ JörgWMittag: deux piles sont aussi bonnes qu'une bande. Main dans la main: utilisez une pile à gauche de la bande et l'autre pile à droite par rapport à votre position actuelle. Donc, un 2-PDA est une machine de Turing. Pour les primitives, il vous suffit de pouvoir extraire une valeur d'une pile et de la pousser sur l'autre, ce qui vous permet de vous déplacer le long de votre bande.
Steve Jessop
20

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 quels que soient les caractères entourant le non-terminal.

Prenez en compte les règles suivantes (minuscules sont les terminaux, majuscules sont non finales):

A -> a
AB -> a

Dans la première règle, vous pouvez remplacer un A indépendamment de ce qui apparaît autour de lui (contexte). Dans la deuxième règle, vous ne pouvez pas remplacer Asauf si elle est suivie par B. Alors que les deux terminaux seront remplacés dans ce cas, le point important est que les terminaux entourent la Aquestion. On ne peut pas remplacer BApar a, ou Bpar a: seulement un Asuivi d'un, Bcar l'ordre, le contexte des non-terminaux est important. Cela signifie que le contexte d'un non terminal compte dans la deuxième règle, ce qui le rend sensible au contexte, tandis que la première règle est sans contexte.


la source
C'est une très bonne explication, bien que je ne sois pas qualifié pour en garantir l'exactitude ou l'exhaustivité. Est-ce tout ce qu'il y a à faire?
Rick
1
Les grammaires informatiques font partie de la hiérarchie de Chomsky . Cet article est un bon point de départ. En outre, ce sujet devrait faire partie de tout programme de baccalauréat en informatique. À tout le moins, les universités devraient enseigner des grammaires régulières et sans contexte, celles-ci comprenant la très grande majorité des langues que nous, programmeurs, sommes susceptibles de rencontrer.
@Snowman: Très crisp.It serait mieux si vous dites que « vous ne pouvez pas dérivez à apartir de ABmoins Aest suivie au Blieu de dire « vous ne pouvez pas remplacer A» , qui pourrait ne pas être possible , car en fait vous remplacez ABn'est pas il?
justin
@justin correct. J'ai mis à jour ma réponse pour être plus clair à ce sujet.
@Snowman: Voulez-vous remplacer Aou ABdans la deuxième règle (sensible au contexte)? Je pense que vous essayez toujours de remplacer, Acomme indiqué dans votre réponse.
Justin
7

Pour comprendre la distinction et la terminologie mieux, il est une bonne idée de comparer un langage hors-contexte comme un n b n avec un contexte sensible 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, par exemple.) Par exemple, aabbcou aabbbccn'est pas dans la dernière langue, alors que aabbcc.

Un accepteur pour la langue sans contexte un n b n peut contracter une paire de aet bquel que soit ce qui est autour d' elle (c. -à- quel que soit le contexte dans lequel ab apparaît) et il fonctionne correctement, accepter que des chaînes dans la langue et le rejet de toute autre chose, c'est-à-dire que la grammaire est S -> aSb | ab. Notez qu'il n'y a pas de terminaux sur le côté gauche de la (des) production (s) . (Il existe deux règles de production, mais nous les écrivons simplement de manière compacte.) L'accepteur peut en principe prendre une décision locale, sans contexte.

En revanche, vous ne pouvez pas faire quelque chose comme ça pour la langue contextuelle a n b n c n , parce que , pour celui- ci , vous devez vous rappeler en quelque sorte le contexte que vous étiez, à savoir le nombre de contractions ab que 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 les terminaux et les non-terminaux à gauche dans les deux dernières règles. Les terminaux de gauche représentent le contexte dans lequel les non-terminaux peuvent être développés.


Bootnote concernant la terminologie «contrat» vs «développer», etc.: bien que les grammaires formelles soient [formellement, hah] génératives, la façon dont elles sont implémentées dans les analyseurs syntaxiques est réellement réductionniste, c'est-à-dire que vous communiquez 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 de décalage-réduction car vous ne pouvez pas décider quelle règle appliquer), mais les deux précédentes les grammaires suffisent à illustrer la distinction entre sans contexte et sensible au contexte. La question de l’ambiguïté dans les grammaires sans contexte est assez compliquée, et ce n’est pas vraiment le sujet de cette question, je ne vais donc pas en dire plus ici, d’autant plus que c’est parce que Wikipédia a un bon article à ce sujet.. En revanche, ses articles sur les environnements sans contexte et en particulier celui sur les langages sensibles au contexte sont! @ # $ @! # $, Surtout si vous êtes novice dans le sujet ... Je suppose que cela fait davantage partie de ma liste de choses à faire.

Pétiller
la source
5

Les réponses ci-dessus donnent une très bonne définition de ce que c'est. Voyons si je peux le formuler avec mes propres mots, pour que vous ayez 23 explications au lieu de 20. Le but d'une grammaire, toute grammaire, est de déterminer si une phrase particulière est une phrase dans la langue donnée. Cependant, ce que nous utilisons réellement pour les grammaires et l'analyse syntaxique est de comprendre le sens de la phrase. C'est comme le vieux diagramme d'une phrase que vous pourriez avoir ou non fait en classe d'anglais à l'école. Une phrase est composée d'une partie de sujet et d'une partie de prédicat, une partie de sujet a un nom et peut-être quelques adjectifs, une partie de prédicat a un verbe et peut-être un nom d'objet, avec quelques adjectifs supplémentaires, etc.

S'il existait une grammaire anglaise (et je ne pense pas qu'il en existe, pas au sens informatique), elle aurait des règles de la forme suivante, appelées productions.

Sentence -> SubjectPart PredicatePart
SubjectPart -> Adjective Noun

etc...

Vous pouvez ensuite écrire un programme et lui transmettre n'importe quelle phrase. Le programme pourrait également utiliser la grammaire pour déterminer la partie de la phrase de chaque mot et la relation entre eux.

Si dans chaque production, il n'y a qu'une chose du côté gauche, cela signifie que chaque fois que vous voyez le côté droit de la phrase, vous êtes autorisé à le remplacer par le côté gauche. Par exemple, chaque fois que vous voyez un nom adjectif, vous pouvez dire "Voilà un sujet" sans faire attention à rien 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 pourrait s'agir d'un NounPhrase dans un PredicatePart. Ça dépend du contexte. Développons un peu notre pseudo-grammaire anglaise:

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

Vous ne pouvez créer un "nom adjectif" dans un 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. Il suffit de vérifier s’il ya plus d’un symbole à gauche des flèches.

Toute langue peut être décrite par plus d'une grammaire. Si une grammaire pour une langue est sans contexte, la langue est sans contexte. On peut prouver pour certaines langues qu’il n’ya pas de grammaire sans contexte possible. Je suppose qu'il pourrait exister une grammaire sans contexte pour le sous-ensemble de pseudo-anglais simplifié que je décris ci-dessus.

Pour ce qui est de savoir pourquoi cela compte, il faut un type de programme plus simple pour analyser une grammaire sans contexte. Comme indiqué dans les autres réponses, il n’est pas nécessaire de disposer de toute la puissance d’une machine de Turing pour analyser une grammaire sans contexte. Un analyseur syntaxique LR (1) à la recherche (qui est une sorte de machine à dérouler) pour une grammaire particulière sans contexte peut analyser n'importe quelle phrase de cette grammaire dans le temps et dans l'espace linéairement à la longueur de la phrase. Si la phrase est dans le langage, l'analyseur produira un arbre de structure identifiant la signification de chaque symbole dans la phrase (ou au moins le rôle qu'il joue dans la structure). Si la phrase ne figure pas dans la grammaire, l'analyseur remarquera et s'arrêtera sur le premier symbole impossible à réconcilier avec la grammaire et les symboles précédents (sur le premier "erreur").

Ce qui est encore mieux, c’est qu’il existe des programmes pour décrire une grammaire et une liste d’instructions sur ce qu’il faut faire avec chaque partie (en un sens associant un "sens" à chaque production) et que le programme écrira l’analyseur. pour vous. Le programme va analyser la phrase, trouver la structure et exécuter vos instructions sur chaque partie de la structure. Ce type de programme s'appelle un analyseur-générateur ou un compilateur-compilateur.

Ce type d’analyse linguistique a été inventé pour l’analyse automatique du langage naturel (comme l’anglais), mais il s’avère que c’est très utile pour l’analyse des langages informatiques. Un concepteur de langage peut écrire une grammaire qui capture son nouveau langage, puis l'exécuter à travers l'analyseur-générateur pour obtenir un programme qui analyse son langage et qui 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 dans lequel il est nécessaire de déclarer toutes les variables avant de les utiliser est sensible au contexte. L'analyseur est une partie du compilateur, mais une logique supplémentaire est nécessaire pour appliquer ces autres exigences. Ce que vous devez ensuite faire, c'est écrire une grammaire qui capture le plus possible votre langage, l'exécuter à l'aide d'un générateur d'analyseurs syntaxiques, puis écrire un 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 syntaxique 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 permettant de transformer une grammaire contextuelle en un programme pour une machine de Turing, en ce sens qu'une LR (1 ) générateur crée des tables d'analyse pour une machine à pression. À mon avis, les tables sous-jacentes à l'analyseur seraient exponentiellement plus grandes. Dans tous les cas, les élèves CS (comme moi, à l'époque) apprennent généralement des grammaires sans contexte et des générateurs d'analyseurs LR (1) tels que YACC.

kwan3217
la source
-1

Les grammaires sans contexte ne considèrent aucun contexte pour les règles de production. Les contextes sont des terminaux ou des non-terminaux.

Donc: Les grammaires sans contexte n'ont qu'un seul non-terminal du côté gauche des règles de production.

Martin Thoma
la source
3
Qu'est-ce que cela ajoute aux réponses existantes? En outre, une règle de production avec deux non-terminaux ou plus du côté gauche n'est pas non plus dépourvue de contexte.
Je pense que les réponses données sont beaucoup trop longues. Si l'on ajoute un TL; DR, je supprimerais celui-ci.
Martin Thoma
Agréable! Diriez-vous que le "contexte" est constitué des caractères supplémentaires qui sont pris en compte lorsque chaque règle de production peut être appliquée?
Rick