Layman'ın terimleriyle, özyineleme nedir?


12

Code.google.com adresindeki bir sayfaya göre , "sol özyineleme" şu şekilde tanımlanır:

Sol özyineleme, yalnızca kendisini içeren bir cümle formu ürettiğinde, üretim kuralının solunda kendisinin yeni bir kopyasının göründüğü yinelemeli nonterminal anlamına gelir.

Wikipedia iki farklı tanım sunar:

  1. Bağlamdan bağımsız dilbilgisi açısından, r'nin herhangi bir prodüksiyonunda ('alternatifler') herhangi birindeki en soldaki simge (doğrudan / anında sol-özyinelemeli) veya başka bir terminal olmayan aracılığıyla, terminal olmayan bir r yinelemelidir. tanımlar (dolaylı / gizli sol özyinelemeli) yeniden r'ye yazar.

  2. "Sonunda sol sembol olarak kendisiyle birlikte duygusal bir form türetecek olan terminal A olmayan bir şey bulabilirsek, bir dilbilgisi sol yinelemelidir."

Burada dil oluşturma ile zar zor başlıyorum ve boş zamanlarımda yapıyorum. Ancak bir dil ayrıştırıcısının seçilmesi söz konusu olduğunda, bu ayrıştırıcı tarafından bırakılan özyinelemenin desteklenip desteklenmediği veya ayrıştırıcının hemen öne ve ortaya çıktığı bir sorundur . "Duygusal form" gibi terimlere bakmak sadece daha fazla jargon listesine yol açar, ancak "sol" özyineleme ayrımı neredeyse çok basit bir şey olmak zorundadır. Çeviri lütfen?

Yanıtlar:


21

REşleşip Reşleşmeyeceğini bulmak için , önce eşleşip eşleşmeyeceğini öğrenmek zorunda kalırsanız, kural yinelemelidir R. Bu R, doğrudan veya dolaylı olarak, kendi üretiminin ilk döneminde ortaya çıktığında olur .

Matematiksel ifadeler için dilbilgisinin oyuncak bir versiyonunu düşünün, dikkat dağılmasını önlemek için sadece ekleme ve çarpma ile:

Expression ::= Multiplication '+' Expression
            || Multiplication

Multiplication ::= Term '*' Term
                 || Term

Term ::= Number | Variable

Yazıldığı gibi, burada sol özyineleme yok - bu dilbilgisini özyinelemeli bir ayrıştırıcıya aktarabiliriz.

Ama varsayalım ki bu şekilde yazmaya çalıştınız:

Expression ::= Expression '*' Expression
            || Expression '+' Expression
            || Term

Term ::= Number | Variable

Bu bir dilbilgisidir ve bazı ayrıştırıcılar bununla başa çıkabilir, ancak özyinelemeli iniş ayrıştırıcıları ve LL ayrıştırıcıları yapamaz - çünkü için kural kendiliğinden Expressionbaşlar Expression. Özyinelemeli bir ayrıştırıcıda bunun herhangi bir girdi tüketmeden neden sınırsız özyinelemeye yol açtığı açık olmalıdır.

Kuralın doğrudan veya dolaylı olarak kendisine atıfta bulunması önemli değildir; eğer Abununla başlar alternatif vardır Bve Balternatif olduğunu başlar ile Adaha sonra, Ave Bher ikisi de dolaylı-sol özyinelemeli ve onların eşleştirme fonksiyonları ayrıştırıcı bir özyinelemeli-iniş sonsuz karşılıklı özyineleme yol açacaktır.


Eğer sonra çok ilk şey değişti Yani ikinci örnekte, ::=gelen Expressionetmek Termve ilk sonra aynı yaptıysa ||, artık sol özyinelemeli olurdu? Yalnızca yaptıysa Ama bundan sonra ::=, ancak ||, yine de sol özyinelemeli olurdu?
Panzercrisis

Bir çok ayrıştırıcının soldan sağa gittiğini, her simgede durduğunu ve yerinde tekrar tekrar değerlendirdiğini söylüyorsunuz. Bu durumda, eğer ilk Expressiondışarı açık olması idi Termsonrasında, ::=ilk sonra ||, her şey iyi olurdu; çünkü er ya da geç, ne bir Numberne de bir şeyle karşılaşır Variable, böylece bir şeyin Expressiondaha fazla yürütülmeden bir şey olmadığını belirleyebilir ...
Panzercrisis

... Ama bunlardan biri hala başlamışsa Expression, potansiyel olarak bir şey olmayan bir şey bulur Termve her şeyin tekrar tekrar olup olmadığını kontrol etmeye devam eder Expression. Bu mu?
Panzercrisis

1
@Panzercrisis az çok. Gerçekten LL, LR ve özyinelemeli iniş ayrıştırıcılarının anlamlarına bakmanız gerekiyor.
Ocaklar

Bu teknik olarak doğrudur, ancak belki de yeterince basit değildir (layman'ın terimleri). Uygulamada, LL ayrıştırıcılarının tipik olarak özyinelemeyi algılama ve bundan kaçınma yeteneğine sahip olacağını da ekleyeceğim (işlemde geçerli olan tutarlı dizeleri potansiyel olarak reddetme) ve pratikte çoğu programlama dilinin bir dilbilgisi tanımlanmış olması Sonsuz özyinelemeyi önleyecek bir yol.

4

Layman'ın şartlarına uydurmaktan bir bıçak alacağım.

Ayrıştırma ağacı açısından düşünürseniz (AST değil, ayrıştırıcının ziyareti ve girdinin genişlemesi), sol özyineleme sola ve aşağı doğru büyüyen bir ağaç ile sonuçlanır. Doğru özyineleme tam tersidir.

Örnek olarak, bir derleyicideki ortak dilbilgisi bir öğeler listesidir. Dizelerin bir listesini ("kırmızı", "yeşil", "mavi") alıp ayrıştıralım. Dilbilgisini birkaç yolla yazabilirim. Aşağıdaki örnekler sırasıyla doğrudan sol veya sağ özyinelemelidir:

arg_list:                           arg_list:
      STRING                              STRING
    | arg_list ',' STRING               | STRING ',' arg_list 

Bu ayrıştırma için ağaçlar:

         (arg_list)                       (arg_list)
          /      \                         /      \
      (arg_list)  BLUE                  RED     (arg_list)
       /       \                                 /      \
   (arg_list) GREEN                          GREEN    (arg_list)
    /                                                  /
 RED                                                BLUE

Özyineleme yönünde nasıl büyüdüğüne dikkat edin.

Bu gerçekten bir sorun değil, ayrıştırıcı aracınız bu sorunu çözebiliyorsa, bir özyinelemeli dilbilgisi yazmak iyi olur. Aşağıdan yukarıya ayrıştırıcılar iyi işliyor. Böylece daha modern LL ayrıştırıcılar olabilir. Özyinelemeli dilbilgisi ile ilgili sorun özyineleme değildir, ayrıştırıcıyı ilerletmeden özyineleme veya bir jeton tüketmeden özyineleme. Dinlediğimizde daima en az 1 jeton tüketirsek, sonunda ayrışmanın sonuna ulaşırız. Sol özyineleme, sonsuz bir döngü olan tüketmeden özyineleme olarak tanımlanır.

Bu sınırlama, saf bir yukarıdan aşağı LL ayrıştırıcısı (özyinelemeli iniş ayrıştırıcısı) ile bir dilbilgisi uygulanmasının bir uygulama detayıdır. Sol özyinelemeli dilbilgileriyle uğraşmak istiyorsanız, tekrarlamadan önce en az 1 jeton tüketmek için üretimi yeniden yazarak bununla başa çıkabilirsiniz, böylece bu, üretken olmayan döngüye asla sıkışmamamızı sağlar. Sol yinelemeli olan herhangi bir dilbilgisi kuralı için, yinelemeli prodüksiyonlar arasında bir jeton tüketerek dilbilgisini yalnızca bir ileriye doğru düzleştiren bir ara kural ekleyerek yeniden yazabiliriz. (NOT: Dilbilgisini yeniden yazmanın tek yolu veya tercih edilen yol olduğunu söylemiyorum, sadece genelleştirilmiş kuralı göstererek. Bu basit örnekte, en iyi seçenek sağ özyinelemeli formu kullanmaktır). Bu yaklaşım genelleştirildiğinden, bir ayrıştırıcı jeneratör (teorik olarak) programlayıcıyı dahil etmeden uygulayabilir. Uygulamada, ANTLR 4'ün şimdi tam da bunu yaptığını düşünüyorum.

Yukarıdaki dilbilgisi için, sol özyineleme gösteren LL uygulaması şöyle görünecektir. Ayrıştırıcı, bir listeyi tahmin etmekle başlar ...

bool match_list()
{
    if(lookahead-predicts-something-besides-comma) {
       match_STRING();
    } else if(lookahead-is-comma) {
       match_list();   // left-recursion, infinite loop/stack overflow
       match(',');
       match_STRING();
    } else {
       throw new ParseException();
    }
}

Gerçekte, gerçekten uğraştığımız şey "saf uygulama" dır. başlangıçta belirli bir cümleyi belirledik, sonra tekrarlayarak bu tahminin işlevini çağırdık ve bu işlev naif olarak aynı öngörüyü tekrar çağırıyor.

Aşağıdan yukarıya ayrıştırıcıların her iki yönde de yinelenen kurallar sorunu yoktur, çünkü bir cümlenin başlangıcını yeniden çözmezler, cümleyi tekrar bir araya getirerek çalışırlar.

Bir dilbilgisinde özyineleme yalnızca yukarıdan aşağıya ürettiğimizde bir sorundur, yani. ayrıştırıcımız, jetonları tüketirken tahminlerimizi "genişleterek" çalışır. Genişletmek yerine, bir LALR (Yacc / Bison) aşağıdan yukarıya ayrıştırıcıda olduğu gibi daraltırız (üretimler "azalır"), o zaman her iki tarafın tekrarlaması bir sorun değildir.

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.