Perché le grammatiche ambigue sono cattive?


30

Capisco che se esistono 2 o più alberi di derivazione sinistra o destra, la grammatica è ambigua, ma non riesco a capire perché sia ​​così grave che tutti vogliono liberarsene.


1
Correlato ma non identico: softwareengineering.stackexchange.com/q/343872/206652 (dichiarazione di non responsabilità: ho scritto la risposta accettata)
marstato


1
In effetti la forma inequivocabile è migliore per usi pratici, la forma inequivocabile utilizza un numero inferiore di regole di produzione costruisce un albero più piccolo in alto (quindi un compilatore efficiente richiede meno tempo per analizzare). La maggior parte degli strumenti fornisce capacità di risolvere ambiguità esplicitamente fuori dalla grammatica laterale.
Grijesh Chauhan il

3
"tutti vogliono liberarsene". Bene, questo non è vero. Nelle lingue commercialmente rilevanti, è comune vedere l'ambiguità aggiunta man mano che le lingue si evolvono. Ad esempio, C ++ ha aggiunto intenzionalmente l'ambiguità std::vector<std::vector<int>>nel 2011, che prima richiedeva uno spazio tra di loro >>. L'intuizione chiave è che queste lingue hanno molti più utenti rispetto ai fornitori, quindi risolvere un piccolo fastidio per gli utenti giustifica un sacco di lavoro da parte degli implementatori.
MSalters l'

Risposte:


52

Considera la seguente grammatica per le espressioni aritmetiche:

XX+XXXXXX/Xvarconst
Considera la seguente espressione:
abc
Qual è il suo valore? Ecco due possibili alberi di analisi:

(X - X) - X inserisci qui la descrizione dell'immagine

Secondo quello a sinistra, dovremmo interpretare abc come (ab)c , che è la solita interpretazione. Secondo quello a destra, dovremmo interpretarlo come a(bc)=ab+c , che probabilmente non è quello che si intendeva.

Durante la compilazione di un programma, vogliamo che l'interpretazione della sintassi sia inequivocabile. Il modo più semplice per imporlo è usare una grammatica non ambigua. Se la grammatica è ambigua, possiamo fornire regole vincolanti, come la precedenza dell'operatore e l'associatività. Queste regole possono essere espresse in modo equivalente rendendo la grammatica inequivocabile in un modo particolare.


Alberi di analisi generati utilizzando il generatore dell'albero di sintassi .


12
@HIRAKMONDAL Il fatto che la sintassi sia ambigua non è un vero problema. il problema è che i due diversi alberi di analisi hanno comportamenti diversi. Se la tua lingua ha una grammatica ambigua ma tutti gli alberi di analisi per un'espressione sono semanticamente equivalenti, non sarebbe un problema (ad esempio, prendi l'esempio di Yuval e considera il caso in cui il tuo unico operatore +).
Bakuriu,

14
@Bakuriu Quello che hai detto è vero, ma "semanticamente equivalente" è un ordine elevato. Ad esempio, l'aritmetica in virgola mobile non è in realtà associativa (quindi i due alberi "+" non sarebbero equivalenti). Inoltre, anche se la risposta è arrivata allo stesso modo, l'ordine di valutazione indefinito è molto importante nelle lingue in cui le espressioni possono avere effetti collaterali. Quindi, quello che hai detto è tecnicamente vero, ma in pratica sarebbe molto insolito che l'ambiguità di una grammatica non abbia ripercussioni sull'uso di quella grammatica.
Richard Rast il

Alcune lingue oggigiorno verificano l'overflow di numeri interi nelle aggiunte, quindi anche a + b + c per numeri interi dipende dall'ordine di valutazione.
gnasher729,

3
Ancora peggio, in alcuni casi la grammatica non fornisce alcun modo per ottenere il significato alternativo. Ho visto questo nei linguaggi di query, in cui la scelta della grammatica di escape (ad esempio raddoppia il carattere speciale per evitarlo) rende impossibili da esprimere determinate query.
Smetti di fare del male a Monica il

12

Contrariamente alle altre risposte esistenti [ 1 , 2 ], esiste davvero un campo di applicazione, in cui sono utili grammatiche ambigue . Nel campo dell'elaborazione del linguaggio naturale (NLP), quando si desidera analizzare il linguaggio naturale (NL) con grammatiche formali, si ha il problema che la NL è intrinsecamente ambigua su diversi livelli [adattato da Koh18, cap. 6.4]:

  • Ambuigità sintattica:

    Peter inseguì l'uomo con la macchina sportiva rossa

    Peter o l'uomo era nella macchina sportiva rossa?

  • Ambuigità semantica:

    Peter andò in banca

    Una banca su cui sedersi o una banca da cui prelevare denaro?

  • Ambiguità pragmatica:

    Due uomini portavano due borse

    Portavano insieme le borse o ognuno portava due borse?

Approcci diversi per la PNL riguardano in modo diverso l'elaborazione in generale e in particolare queste ambuità. Ad esempio, la pipeline potrebbe apparire come segue:

  1. Parse NL con grammatica ambigua
  2. Per ogni AST risultante: eseguire la generazione di modelli per generare significati semantici ambigui e escludere ambiguità sintattiche impossibili dal passaggio 1
  3. Per ogni modello risultante: salvalo nella cache.

Fai questa pipeline per ogni frase. Più testo, diciamo, dallo stesso libro che elabori, più puoi escludere modelli superflui impossibili, che sono sopravvissuti fino al passaggio 3, dalle frasi precedenti.

A differenza del linguaggio di programmazione, possiamo lasciar andare il requisito che ogni frase NL abbia una semantica precisa. Invece, possiamo semplicemente tenere in contabilità più modelli semantici possibili durante l'analisi di testi più grandi. Di tanto in tanto, approfondimenti successivi ci aiutano a escludere le precedenti ambiguità.

Se vuoi sporcarti le mani con i parser in grado di produrre più derivazioni per una grammatica ambigua, dai un'occhiata al Grammatical Framework . Inoltre, [Koh18, cap. 5] ha un'introduzione ad esso che mostra qualcosa di simile alla mia pipeline sopra. Si noti tuttavia che poiché [Koh18] sono appunti di una lezione, le note potrebbero non essere così facili da capire da sole senza le lezioni.


Riferimenti

[Koh18]: Michael Kohlhase. "Elaborazione del linguaggio naturale basato sulla logica. Semestre invernale 2018/19. Appunti della lezione." URL: https://kwarc.info/teaching/LBS/notes.pdf . URL della descrizione del corso: https://kwarc.info/courses/lbs/ (in tedesco)

[Koh18, cap. 5]: Vedi capitolo 5, "Implementazione di frammenti: strutture grammaticali e logiche", in [Koh18]

[Koh18, cap. 6.4] Vedi capitolo 6.4, "Il ruolo computazionale delle ambiguità", in [Koh18]


Grazie mille .. Avevo lo stesso dubbio e l'
hai

1
Per non parlare dei problemi con il bufalo di bufalo di bufalo Bufalo di bufalo di bufalo ... per un numero adeguato di bufali
Hagen von Eitzen

Scrivi "al contrario", ma definirei l'altro lato della medaglia da quello che ho risposto. Analizzare le lingue naturali con le loro grammatiche ambigue è così difficile che i parser tradizionali non possono farlo!
Davislor,

1
@ComFreek Dovrei essere più preciso qui. Un breve sguardo a GF (Grazie per il link!) Mostra che legge grammatiche senza contesto con tre estensioni (come consentire la riduplicazione) e restituisce un elenco di tutte le possibili derivazioni. Gli algoritmi per farlo sono in circolazione dagli anni '50. Tuttavia, essere in grado di gestire CFG completamente generali significa che il runtime nel caso peggiore esplode e, in pratica, anche quando si utilizza un parser generale come GLL, gli ingegneri del software cercano di utilizzare un sottoinsieme di CFG, come le grammatiche LL, che può essere analizzato in modo più efficiente.
Davislor,

1
@ComFreek Quindi non è che i computer non siano in grado di gestire la CFG (anche se i linguaggi naturali non sono realmente privi di contesto e la traduzione automatica realmente utile utilizza tecniche completamente diverse). È che, se richiedi al tuo parser di gestire l'ambiguità, questo esclude alcune scorciatoie che lo avrebbero reso più efficiente.
Davislor,

10

Anche se esiste un modo ben definito per gestire l'ambiguità (le espressioni ambigue sono errori di sintassi, ad esempio), queste grammatiche continuano a causare problemi. Non appena si introduce l'ambiguità in una grammatica, un parser non può più essere sicuro che la prima corrispondenza che ottiene sia definitiva. Deve continuare a provare tutti gli altri modi per analizzare un'affermazione, per escludere qualsiasi ambiguità. Inoltre non hai a che fare con qualcosa di semplice come un linguaggio LL (1), quindi non puoi utilizzare un parser semplice, piccolo e veloce. La tua grammatica ha simboli che possono essere letti in diversi modi, quindi devi essere pronto a tornare indietro molto.

In alcuni domini con restrizioni, potresti essere in grado di cavartela dimostrando che tutti i modi possibili per analizzare un'espressione sono equivalenti (ad esempio perché rappresentano un'operazione associativa). (a + b) + c = a + (b + c).


9

fa IF a THEN IF b THEN x ELSE ymedia

IF a THEN
    IF b THEN
        x
    ELSE
        y

o

IF a THEN
    IF b THEN x
ELSE
    y

? AKA il problema penzolante .


1
Questo è un buon esempio che mostra che anche una grammatica non ambigua (come in Java, C, C ++, ...) consente apparenti ambiguità (!) Da una prospettiva umana. Anche se formalmente e computazionalmente bene, ora abbiamo più problemi di sviluppo senza UX / bug.
ComFreek,

5

Prendi ad esempio l'analisi più fastidiosa in C ++:

bar foo(foobar());

È una dichiarazione foodi tipo di funzione bar(foobar())(il parametro è un puntatore a funzione che restituisce a foobar) o una dichiarazione foodi tipo variabile inte inizializzata con un valore predefinito inizializzato foobar?

Ciò si differenzia nei compilatori assumendo il primo a meno che l'espressione all'interno dell'elenco dei parametri non possa essere interpretata come un tipo.

quando ottieni un'espressione così ambigua il compilatore ha 2 opzioni

  1. supponiamo che l'espressione sia una derivazione particolare e aggiungi un po 'di chiarimento alla grammatica per consentire l'espressione dell'altra derivazione.

  2. errore e richiedere chiarimento in entrambi i modi

Il primo può cadere naturalmente, il secondo richiede che il programmatore del compilatore sia a conoscenza dell'ambiguità.

Se questa ambiguità rimane inosservata, è possibile che 2 compilatori diversi impostino automaticamente derivazioni diverse per quell'espressione ambigua. Il risultato è che il codice non è portatile per ragioni non ovvie. Il che porta le persone ad assumere che sia un bug in uno dei compilatori mentre in realtà è un errore nelle specifiche del linguaggio.


5

Penso che la domanda contenga un'ipotesi che nella migliore delle ipotesi sia solo borderline corretta.

Nella vita reale è abbastanza comune vivere semplicemente con grammatiche ambigue, purché non siano (per così dire) troppo ambigue.

Ad esempio, se ti guardi intorno alle grammatiche compilate con yacc (o simili, come bisonti o byacc) scoprirai che alcuni generano avvertimenti su "N spostamenti / riduci conflitti" quando li compili. Quando yacc incontra uno spostamento / riduzione del conflitto, ciò indica un'ambiguità nella grammatica.

Un conflitto di spostamento / riduzione, tuttavia, è di solito un problema piuttosto lieve. Il generatore di parser risolverà il conflitto a favore dello "spostamento" anziché della riduzione. La grammatica va benissimo se è quello che vuoi (e in pratica sembra funzionare perfettamente).

Un conflitto di spostamento / riduzione si presenta in genere in un caso in questo ordine generale (utilizzando i limiti per i non terminali e le lettere minuscole per i terminali):

A -> B | c
B -> a | c

Quando incontriamo un c, c'è un'ambiguità: dovremmo analizzare il cdirettamente come un A, o dovremmo analizzarlo come un B, che a sua volta è un A? In un caso come questo, yacc e tali sceglieranno il percorso più semplice / breve e analizzeranno cdirettamente il percorso come A, piuttosto che seguire il percorso c-> B-> A. Questo può essere sbagliato, ma in tal caso, probabilmente significa che hai un errore molto semplice nella tua grammatica e non dovresti consentire l' copzione come possibilità per Atutti.

Ora, al contrario, potremmo avere qualcosa di più simile a questo:

A -> B | C
B -> a | c
C -> b | c

Ora quando incontriamo un cabbiamo un conflitto tra se trattare ccome un Bo un C. Ci sono molte meno possibilità che una strategia di risoluzione automatica dei conflitti scelga ciò che vogliamo veramente. Nessuno di questi è uno "spostamento" - entrambi sono "riduzioni", quindi si tratta di un "ridurre / ridurre il conflitto" (che coloro che sono abituati allo yacc e generalmente riconoscono come un problema molto più grande di uno spostamento / riduzione del conflitto).

Quindi, anche se non sono sicuro che sarei arrivato al punto di dire che qualcuno apprezza davvero l' ambiguità nella loro grammatica, in almeno alcuni casi è abbastanza piccolo che nessuno se ne preoccupi davvero tanto. In astratto potrebbe piacere l'idea di rimuovere ogni ambiguità, ma non abbastanza per farlo sempre. Ad esempio, una grammatica piccola e semplice che contiene un'ambiguità minore può essere preferibile a una grammatica più ampia e complessa che elimina l'ambiguità (specialmente quando entri nel regno pratico della generazione effettiva di un parser dalla grammatica e scopri che l'ambiguità la grammatica produce un parser che non verrà eseguito sul computer di destinazione).


amico, vorrei aver avuto questa eccellente spiegazione dei conflitti di riduzione dei turni 5 mesi fa! ^^; +1
HotelCalifornia
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.