È noto che i parser di discendenza ricorsiva possono richiedere tempi esponenziali in alcuni casi; qualcuno potrebbe indicarmi i campioni, dove questo accade? Particolarmente interessato ai casi di PEG (cioè con scelte prioritarie).Sulla complessità dei parser di discesa ricorsivi
risposta
È perché è possibile finire per analizzare le stesse cose (controllare la stessa regola nella stessa posizione) molte volte in diversi rami di ricorsione. È un po 'come calcolare il n-esimo numero di Fibonacci usando la ricorsione.
Grammar:
A -> xA | xB | x
B -> yA | xA | y | A
S -> A
Input:
xxyxyy
Parsing:
xA(xxyxyy)
xA(xyxyy)
xA(yxyy) fail
xB(yxyy) fail
x(yxyy) fail
xB(xyxyy)
yA(yxyy)
xA(xyy)
xA(yy) fail
xB(yy) fail
x(yy) fail
xB(xyy)
yA(yy)
xA(y) fail
xB(y) fail
x(y) fail
xA(yy) fail *
x(xyy) fail
xA(yxyy) fail *
y(yxyy) fail
A(yxyy)
xA(yxyy) fail *
xB(yxyy) fail *
x(yxyy) fail *
x(xyxyy) fail
xB(xxyxyy)
yA(xyxyy) fail
xA(xyxyy) *
xA(yxyy) fail *
xB(yxyy) fail *
...
*
- dove analizziamo una regola nella stessa posizione in cui abbiamo già analizzato in un ramo diverso. Se avessimo salvato i risultati - quali regole falliscono in quali posizioni - sapremmo che xA (xyxyy) fallisce la seconda volta e non verificheremo di nuovo l'intera sottostruttura. Non volevo scrivere tutto, ma puoi vedere che ripeterà gli stessi sottoalberi molte volte.
Quando accadrà, quando ci sono molte trasformazioni sovrapposte. La scelta prioritaria non cambia le cose - se la regola della priorità più bassa finisce per essere l'unica corretta (o nessuna è corretta), devi comunque controllare tutte le regole.
Qualsiasi parser dall'alto verso il basso, inclusa la discesa ricorsiva, può teoricamente diventare esponenziale se la combinazione di input e grammatica è tale da rendere necessario un numero elevato di backtrack. Questo succede se la grammatica è tale che le scelte determinative sono poste alla fine di lunghe sequenze. Ad esempio, se hai un simbolo come & che significa "tutti gli altri precedenti sono effettivamente positivi" e poi hai dati come "((((a - b) - c) - d) - e &" allora il parser deve andare all'indietro e cambia tutti i minimi rispetto ai negativi. Se inizi a creare espressioni nidificate lungo queste linee, puoi creare un insieme di input efficacemente non terminante.
Devi capire che stai entrando in una questione politica qui, perché la realtà è che la maggior parte delle grammatiche e dei set di dati normali non sono così, tuttavia, ci sono MOLTE persone che sistematicamente biforcano la discendenza ricorsiva perché non è facile da rendere automaticamente RD. Tutti i parser iniziali sono LALR perché sono MOLTO più facili da eseguire automaticamente rispetto a RD. Quindi, quello che è successo è che tutti hanno appena scritto LRD e RD a bocca aperta, perché nei vecchi tempi l'unico modo per creare un RD era codificarlo a mano. Ad esempio, se leggi il libro del drago troverai che Aho & Ullman scrive solo un paragrafo su RD, ed è fondamentalmente solo un takedown ideologico che dice "RD è cattivo, non farlo".
Ovviamente, se si avvia RD a mano (come ho fatto), si scoprirà che sono molto meglio dei LALR per una serie di motivi. Ai vecchi tempi si poteva sempre dire a un compilatore che aveva un RD codificato a mano, poiché aveva messaggi di errore significativi con precisione di localizzazione, mentre i compilatori con LALR mostravano l'errore che si verificava a 50 linee di distanza da dove realmente si trovava. Le cose sono cambiate molto dai tempi antichi, ma dovresti rendertene conto quando inizi a leggere il FUD su RD, che proviene da una lunga tradizione di "trascrizione verbale" in "determinati cerchi".
- 1. Complessità di run-time per algoritmi ricorsivi
- 2. Haskell - parser di discesa ricorsivo
- 3. Limitazioni dei generatori di parser e parser PEG?
- 4. Complessità del tempo dei metodi HashMap
- 5. Quali garanzie ci sono sulla complessità in fase di esecuzione (Big-O) dei metodi LINQ?
- 6. Percorsi ricorsivi in Rails
- 7. Menu a discesa sulla barra delle azioni
- 8. tipi di dati ricorsivi templated
- 9. Come si confrontano i parser Pratt con altri tipi di parser e perché vengono utilizzati così poco?
- 10. Generatore di parser/parser combinato
- 11. Generatori ricorsivi in Python
- 12. Oggetti ricorsivi in F #?
- 13. ricorsivi lambda in F #
- 14. Laravel Rapporti ricorsivi
- 15. Sono possibili DataTemplates ricorsivi?
- 16. Tipi generici ricorsivi
- 17. Tipi ricorsivi in OCaml?
- 18. Knockout.js modificare i valori discesa possibili sulla base di un'altra discesa
- 19. Riempi elenco a discesa sulla selezione di un altro elenco a discesa
- 20. JavaScript: impostare discesa elemento selezionato sulla base di opzione testo
- 21. Disable discesa sulla librazione di bootstrap quando navbar è compresso
- 22. File nascosti cancellati ricorsivi
- 23. Algoritmo di Dijkstra Vs Ricerca uniforme dei costi (Complessità temporale)
- 24. Come ridurre Cyclomatic complessità dei casi di istruzioni switch
- 25. Ottieni l'elenco sulla base dei dati dell'elenco a discesa in asp.net mvc3
- 26. Algoritmica complessità di Data.Hashtable
- 27. Strumenti di analisi della complessità del codice oltre la complessità ciclomatica
- 28. Ottimizzazione della discesa dei gradienti in CUDA
- 29. Algoritmo complessità
- 30. Problema di parser Spirit e Lex parser
Solo se tornano indietro. Se continuano ad andare da sinistra a destra nello stile 'LL (1)' dovrebbero essere * O (N) *. – EJP
@EJP, ovviamente. Ma anche il backtracking non introduce complessità esponenziale nella maggior parte dei casi. Sto cercando di capire meglio, in quali circostanze questo accade. – fithu
Non tutti i parser di discesa ricorsivi possono presentare un comportamento esponenziale. Ad esempio, ANTLR 4 produce parsers di discesa ricorsivi con scelte [semi] prioritizzate, ma è il caso peggiore O (n⁴) (la dimostrazione fa parte di un documento su cui sto lavorando attualmente). –