Sol özyineleme neden kötü?


20

Derleyici tasarımında, dilbilgisinde sol özyineleme neden ortadan kaldırılmalıdır? Sonsuz bir özyinelemeye neden olabileceği için okuyorum, ama doğru bir özyinelemeli dilbilgisi için de doğru değil mi?


2
Genellikle derleyiciler yukarıdan aşağıya ayrıştırma kullanır. Sol özyineleme varsa, ayrıştırıcı sonsuz bir özyinelemeye gider. Ancak, sağ özyinelemede ayrıştırıcı, dizenin önekini şu ana kadar görebilir. Böylece, türetmenin "çok ileri" gittiğini kontrol edebilir. Elbette, rolleri değiştirebilir ve ifadeleri sağdan yorumlayabilir, sağ özyinelemeyi kötüleştirebilir ve sol özyinelemeyi iyi yapabilirsiniz.
Shaull

6
Sol özyineleme kötü çünkü bilgisayarların 16 KB RAM'e sahip olduğu eski günlerde en çok kullanılan ayrıştırıcı jeneratör onunla baş edemedi.
Andrej Bauer

Yanıtlar:


15

Sol özyinelemeli dilbilgileri mutlaka kötü bir şey değildir. Bu dilbilgisi, LR ayrıştırıcısında olduğu gibi zaten ayrıştırılmış ifadeleri takip etmek için bir yığın kullanılarak kolayca ayrıştırılır .

Bir CF dilbilgisi nin sol özyinelemeli kuralının şu şekildedir:G,=(V,Σ,R,,S)

ααβ

ile ait bir eleman ve unsuru . (Başlığın için komple resmi tanımına bakınız vardır ).αVβVΣ(V,Σ,R,,S)

Genellikle, terminallerle terminaller bir dizi aslında, ve bir diğer kural yoktur burada sağ taraftaki görünmez.α αβαα

Dilbilgisi ayrıştırıcısı tarafından yeni bir terminal alındığında (lexer'dan), bu terminal yığının üstüne itilir: bu işleme kaydırma adı verilir .

Bir kuralın sağ tarafı, yığının üstündeki bir ardışık eleman grubu ile her eşleştiğinde, bu grup, yeni eşleşen ifadeyi temsil eden tek bir elemanla değiştirilir. Bu değiştirmeye azaltma denir .

Doğru özyinelemeli dilbilgileriyle, yığın bir azaltma gerçekleşene kadar süresiz olarak büyüyebilir, böylece ayrıştırma olanaklarını oldukça önemli ölçüde sınırlar. Bununla birlikte, sol özyinelemeli olanlar derleyicinin daha önce (aslında, mümkün olan en kısa sürede) indirimler üretmesine izin verecektir. Daha fazla bilgi için wikipedia girişine bakınız .


Değişkenlerinizi tanımlamanız size yardımcı olacaktır.
Andrew S

12

Bu kuralı göz önünde bulundurun:

example : 'a' | example 'b' ;

Şimdi bir LL ayrıştırıcısını 'b'bu kurala benzer , eşleşmeyen bir dizeyle eşleştirmeye çalışın . Yana 'a'uymuyor, bu maç için çalışacağım example 'b'. Ama bunu yapmak için, eşleşmek zorunda example... ilk etapta yapmaya çalıştığı şey buydu. Her zaman aynı jeton akışını aynı kurala eşleştirmeye çalıştığından, sonsuza kadar eşleşip eşleşmeyeceğini denemek sıkışabilir.

Bunu önlemek için, sağdan ayrıştırmanız (gördüğüm kadarıyla oldukça nadirdir ve bunun yerine sorunu doğru özyineleme yapar), izin verilen yuvalama miktarını yapay olarak sınırlamanız veya eşleşmeniz gerekir. özyineleme başlamadan önce bir jeton, böylece her zaman bir temel durum vardır (yani, tüm jetonların tüketildiği ve hala tam bir eşleşme yoktur). Sağ özyinelemeli bir kural üçüncüyü zaten yaptığı için aynı sorunu yoktur.


3
Ayrıştırma işleminin mutlaka yukarıdan aşağıya saf ayrıştırma olduğunu varsayıyorsunuz.
reinierpost

Kolayca önlenebilecek bir sorun olan oldukça yaygın bir ayrıştırma yönteminin bir tuzağına dikkat çekiyorum. Sol özyineleme ile başa çıkmak kesinlikle mümkündür , ancak onu korumak, onu kullanabilen ayrıştırıcı türü üzerinde neredeyse her zaman gereksiz bir sınırlama yaratır.
cHao

Evet, koymanın daha yapıcı ve kullanışlı bir yolu.
reinierpost

4

(Bu sorunun şu ana kadar oldukça eski olduğunu biliyorum, ancak başkalarının da aynı soruyu sorması durumunda ...)

Özyinelemeli iniş ayrıştırıcıları bağlamında mı soruyorsunuz? Örneğin, dilbilgisi için expr:: = expr + term | termneden böyle bir şey (özyinelemeli bıraktı):

// expr:: = expr + term
expr() {
   expr();
   if (token == '+') {
      getNextToken();
   }
   term();
}

sorunlu, ama bu değil (doğru özyinelemeli)?

// expr:: = term + expr
expr() {
   term();
   if (token == '+') {
      getNextToken();
      expr();
   }
}

Her iki expr()çağrının da kendilerine benziyor . Ancak önemli fark bağlamdır - yani özyinelemeli çağrı yapıldığında geçerli jeton.

Sol özyinelemeli durumda, expr()sürekli olarak aynı jetonla çağırır ve ilerleme kaydedilmez. Sağ özyinelemeli durumda, çağrıya term()ulaşmadan önce çağrıdaki bazı girdileri ve ARTI jetonunu tüketir expr(). Bu noktada, özyinelemeli çağrı terimi tekrar arayabilir ve if testine tekrar ulaşmadan önce sonlanabilir.

Örneğin, 2 + 3 + 4'ü ayrıştırmayı düşünün. Sol özyinelemeli ayrıştırıcı expr(), ilk jetona takılı kalırken sonsuz olarak çağrılırken, sağ özyinelemeli ayrıştırıcı expr()tekrar aramadan önce "2 +" tüketir . İkinci çağrı expr()"3 +" ile eşleşir ve expr()sadece 4 tanesiyle çağrılır. 4 bir terimle eşleşir ve ayrıştırma artık çağrı yapılmadan sona erer expr().


2

Bison kılavuzundan:

"Sol özyineleme veya sağ özyineleme kullanılarak her türlü sekans tanımlanabilir, ancak her zaman sol özyineleme kullanmalısınız , çünkü sınırlı yığın alanı olan çok sayıda öğenin bir dizisini ayrıştırabilir. Sağ özyineleme, kuralın bir kez bile uygulanabilmesi için tüm öğelerin yığına kaydırılması gerektiğinden, sıradaki öğelerin sayısı ile orantılıdır. Daha fazla açıklama için bkz. Bizon Ayrıştırıcı Algoritması. "

http://www.gnu.org/software/bison/manual/html_node/Recursion.html

Bu, ayrıştırıcının algoritmasına bağlıdır, ancak diğer cevaplarda belirtildiği gibi, bazı ayrıştırıcılar sol özyineleme ile çalışmayabilir

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.