Ölü kodun derleyiciler tarafından algılanamayacağının kanıtı


32

Birisi derleyici olacak olan çok sayıda konuda bir kış kursu öğretmeyi planlıyorum. Şimdi, çeyrek boyunca vereceğim ödevleri düşünürken bu problemle karşılaştım, ama bunun yerine bir örnek olarak kullanabilmem için beni çok kızdırdı.

public class DeadCode {
  public static void main(String[] args) {
     return;
     System.out.println("This line won't print.");
  }
}

Yukarıdaki programda, print cümlesinin nedeniyle hiçbir zaman yürütülmeyeceği açıktır return. Derleyiciler bazen ölü kodla ilgili uyarılar veya hatalar verir. Örneğin, yukarıdaki kod Java ile derlenmeyecektir. Ancak javac derleyicisi her programdaki tüm ölü kod örneklerini algılamaz. Hiçbir derleyicinin bunu yapamayacağını nasıl kanıtlayabilirim?


29
Arka planınız nedir ve öğreteceğiniz bağlam nedir? Künt olmak için, bunu öğretmek istediğinizi görerek sormak zorunda kaldığınız için endişeliyim. Ama burada sormak iyi çağrı!
Raphael


9
@ MichaelKjörling Bu kodlar olmadan bile ölü kod tespiti mümkün değildir.
David Richerby

2
BigInteger i = 0; while(isCollatzConjectureTrueFor(i)) i++; printf("Hello world\n");
user253751

2
@ immibis Soru , ölü kod algılamanın imkansız olduğuna dair bir kanıt ister . Doğru ölü kod algılamanın matematikte açık bir problem çözmeyi gerektirdiği bir örnek verdiniz . Bu, ölü kod algılamanın imkansız olduğunu kanıtlamaz .
David Richerby 12:15

Yanıtlar:


57

Hepsi durma sorununun kararsızlığından geliyor. "Mükemmel" bir ölü kod fonksiyonuna, bazı Turing Machine M'ye ve bazı giriş dizgisine x sahip olduğumuzu ve buna benzer bir prosedürün olduğunu varsayalım:

Run M on input x;
print "Finished running input";

Eğer M sonsuza dek sürerse, o zaman asla ulaşamayacağımız için print deyimini siliyoruz. M sonsuza dek koşmazsa, print deyimini tutmamız gerekir. Böylece, bir ölü kod çıkarıcımız varsa, aynı zamanda Halting Problemini çözmemize de izin verir, böylece böyle bir ölü kod çıkarıcısının olamayacağını biliyoruz.

Bunu aşmanın yolu “muhafazakar yaklaşım” dır. Dolayısıyla yukarıdaki Turing Machine örneğimde, x üzerinde M çalıştırmanın bitebileceğini varsayabiliriz, bu nedenle güvenli oynar ve print deyimini kaldırmadık. Örneğinizde, hangi fonksiyonların durup durmadığının önemli olmadığı, bu baskı ifadesine ulaşmamızın mümkün olmadığını biliyoruz.

Genellikle, bu bir "kontrol-akış grafiği" yapılarak yapılır. Sonsuza kadar çalışsa veya yalnızca bir kez çalışsa ve her ikisini de ziyaret etmese bile, "bir süre döngüsünün sonu başlangıcı ve sonrasına bağlı" gibi basitleştirici varsayımlar yaparız. Benzer şekilde, if ifadesinin gerçekte hiç kullanılmamış olsa bile, tüm dallarına ulaşabileceğini varsayıyoruz. Bu tür sadeleştirmeler, verdiğiniz örnek gibi "açıkça ölü kodu" kaldırmamıza izin verirken karar vermemize izin verir.

Yorumlardaki bazı kafa karışıklıklarını netleştirmek için:

  1. Nitpick: Sabit M için bu her zaman kararlaştırılabilir. M giriş olmalı

    Raphael’in dediği gibi benim örneğimde Turing Machine’i bir girdi olarak görüyoruz. Buradaki fikir, eğer mükemmel bir DCE algoritmasına sahip olsaydık, herhangi bir Turing Makinesi için verdiğim kod parçacığını kurabilirdik ve bir DCE'ye sahip olmak durma problemini çözebilirdi.

  2. ikna olmadı. Şube yok dümdüz ileri bir uygulamada künt bir ifade olarak geri dönmeye karar vermek zor değil. (ve derleyicim bana bunun çözülebildiğini söylüyor)

    Njzk2 sorunu gündeme geldi: kesinlikle haklısın, bu durumda, iadeye ulaşılmasından sonra bir ifadenin olmadığını belirleyebilirsin. Bunun nedeni, kontrol-akış grafiği kısıtlamaları kullanarak erişilmezliğini tanımlayabileceğimiz kadar basit (yani, bir return ifadesinden hiçbir çıkış kenarı yok). Ancak kullanılmayan tüm kodları ortadan kaldıran mükemmel bir ölü kod giderici yoktur.

  3. Giriş için bağımlı bir kanıt almıyorum. Kodun sınırlı olmasına izin verecek türde bir kullanıcı girişi varsa, derleyicinin aşağıdaki dalın ölmediğini varsayması doğru olur. Tüm bu artıların ne için olduğunu göremiyorum, her ikisi de açık (örneğin, sonsuz stdin) ve yanlış.

    Tomáš Zato için: bu gerçekten girdi bağımlı bir kanıt değil. Aksine, bir "forall" olarak yorumlayın. Aşağıdaki gibi çalışır: mükemmel bir DCE algoritmasına sahip olduğumuzu varsayalım. Bana keyfi bir Turing Makinesi M ve x girişi verirseniz, yukarıdaki kod parçasını oluşturarak ve print ifadesinin kaldırılıp kaldırılmadığını görerek, M'nin durup durmadığını belirlemek için DCE algoritmamı kullanabilirim. Forall ifadesini kanıtlamak için isteğe bağlı bir parametre bırakmak bu teknik matematik ve mantıkta yaygındır.

    TomášZato'nun kodun sonlu olduğu konusundaki noktasını tam olarak anlamadım. Elbette kod sonludur, ancak mükemmel bir DCE algoritması, bir infinte olan tüm koda uygulanmalıdır. Benzer şekilde, kodun kendisi sınırlı olsa da, olası girdi kümeleri, kodun olası çalışma süresi gibi sınırsızdır.

    Ölü olmayan son şubeyi göz önünde bulundurmakla ilgili olarak: bahsettiğim "muhafazakar yaklaşım" açısından güvenlidir, ancak OP'nin istediği gibi tüm ölü kod örneklerini tespit etmek yeterli değildir.

Bunun gibi bir kod düşünün:

while (true)
  print "Hello"
print "goodbye"

Açıkça print "goodbye"programın davranışını değiştirmeden kaldırabiliriz . Böylece, ölü koddur. Ancak (true), whiledurum yerine farklı bir işlev çağrısı varsa , o zaman kaldırabilir miyiz, kararsızlığa yol açıp açamayacağımızı bilmiyoruz.

Bunu kendi başıma yapmayacağımı unutmayın. Derleyiciler teorisinde iyi bilinen bir sonuçtur. Kaplan Kitabında tartışılıyor . ( Google kitaplarında nerede konuştuklarını görebilirsiniz .


1
@ njzk2: Tüm ölü kodları ortadan kaldıran bir ölü kod giderici oluşturmanın imkansız olduğunu göstermeye çalışıyoruz, bazı ölü kodları ortadan kaldıran bir ölü kod giderici oluşturmak imkansız değil . Geri dönüş sonrası baskı örneği kontrol-akış grafiği teknikleri kullanılarak kolayca elimine edilebilir, ancak tüm ölü kodlar bu şekilde elimine edilemez.
user2357112

4
Bu cevap yorumlara referans verir . Cevabı okuduğumda, yorumların içine atlamalı, sonra cevaba geri dönmeliyim. Bu kafa karıştırıcıdır (iki katına kadar yorumların kırılgan ve kaybolabileceğini düşündüğünüzde). Bağımsız bir cevap okumak çok daha kolay olurdu.
Kasım’da

1
@ TomášZato - değişkenini artıran ve tek bir mükemmel sayı olup olmadığını kontrol eden ve yalnızca böyle bir sayı bulduğunda sonlanan programı düşünün . Açıkçası bu program herhangi bir harici girişe bağlı değildir. Bu programın sonlandırılıp sonlandırılmayacağının kolayca belirlenebileceğini mi iddia ediyorsunuz? nn
Gregory J. Puleo,

3
@ TomášZato Durma problemini anlamada yanılıyorsun. Sonlu bir Turing Makinesi ve sonlu giriş verildiğinde , üzerinde üzerinde çalışırken sonsuz döngüler olup olmadığını belirlemek mümkün değildir . Bunu titizlikle kanıtlamadım çünkü tekrar tekrar kanıtlandı ve bilgisayar biliminin temel bir ilkesi. Vikipedi'deki ispatın güzel bir taslağı varMxMx
jmite 12:15

1
jmite, lütfen cevabın kendi içinde kalması için geçerli yorumlarınızı cevaba dahil edin. Ardından, eski olan tüm yorumları, böylece temizleyebilmemiz için işaretleyin. Teşekkürler!
Raphael

14

Bu, jmite'nin sonlandırmama konusundaki potansiyel kafa karışıklığını gideren cevabındaki bir bükülmedir. Kendisini her zaman durduran, ölü kodlu bir program vereceğim, ancak algoritmasına sahip olup olmadığına (her zaman) karar veremiyoruz.

Ölü kod tanımlayıcısı için aşağıdaki girdi sınıflarını göz önünde bulundurun:

simulateMx(n) {
  simulate TM M on input x for n steps
  if M did halt
    return 0
  else
    return 1
}

Yana Mve xsabitlenir, simulateMsölü kodu vardır return 0ve ancak eğer Müzerinde durdurmak değil x.

Bu bize derhal durma probleminden ölü kod kontrolüne bir azalma sağlar: durma problemi örneği olarak TM verildiğinde , kodu ile yukarıda bir program hazırlayın - eğer sadece kendi başına durmazsa ölü kodu vardır kodu.MxMM

Bu nedenle, ölü kod kontrolü hesaplanamaz.

Bu bağlamda ispat tekniği olarak azaltma konusunda bilginiz yoksa referans materyalimizi öneriyorum .


5

Bu tür bir mülkü ayrıntılara dalmadan göstermenin basit bir yolu aşağıdaki lemayı kullanmaktır:

Lemma: Turing-tamamlanmış bir dil için herhangi bir C derleyicisi için undecidable_but_true(), hiçbir argüman almayan ve booleanı doğru döndüren bir işlev vardır, C'nin undecidable_but_true()doğru ya da yanlış olduğunu tahmin edemez .

İşlevin derleyiciye bağlı olduğunu unutmayın. Bir işlev verildiğinde undecidable_but_true1(), bir derleyiciye bu işlevin doğru mu yanlış mı döndüğü bilgisi ile her zaman artırılabilir; ama her zaman undecidable_but_true2()kapsanmayacak başka bir işlev vardır.

Kanıt: tarafından Rice'ın teoremi , mülkiyet “gerçek bu işlev döndürür” undecidable. Bu nedenle, herhangi bir statik analiz algoritması, tüm olası işlevler için bu özelliğe karar veremez.

Sonuç: Derleyici C verildiğinde, aşağıdaki program tespit edilemeyen bir ölü kod içeriyor:

if (!undecidable_but_true()) {
    do_stuff();
}

Java ile ilgili bir not: Java dili, derleyicilerin erişilemeyen kod içeren bazı programları reddetmesini zorunlu kılarken, bu kodun erişilebilir olan tüm noktalarda (örneğin geçersiz olmayan bir işlevdeki kontrol akışı bir returnifade ile bitmelidir ) kesin olarak zorunlu kılınmasını zorunlu kılar . Dil tam olarak erişilemeyen kod analizinin nasıl yapıldığını belirtir; o olmasaydı taşınabilir programlar yazmak imkansız olurdu. Formun bir program verilen

some_method () {
    <code whose continuation is unreachable>
    // is throw InternalError() needed here?
}

hangi durumlarda erişilemez kodun başka bir kodla takip edilmesi gerektiğini ve hangi durumlarda herhangi bir kodla takip edilmemesi gerektiğini belirtmek gerekir. Java 101'de, erişilemeyen ancak Java derleyicilerinin fark etmesine izin verecek şekilde olmayan kod içeren bir Java programı örneği şöyledir:

String day_of_week(int n) {
    switch (n % 7) {
    case 0: return "Sunday";
    case 1: case -6: return "Monday";
    …
    case 6: case -1: return "Saturday";
    }
    // return or throw is required here, even though this point is unreachable
}

Bazı diller için bazı derleyicilerin, sonuna day_of_weekerişilemez olduğunu tespit edebileceğini unutmayın .
user253751 12:15

@ immibis Evet, örneğin CS101 öğrencileri deneyimlerime göre yapabilir (kuşkusuz CS101 öğrencileri sağlam bir statik analizör olmasa da, genellikle olumsuz vakaları unuturlar). Bu benim amacımın bir parçası: Java derleyicisinin algılamayacağı kodlara erişilemez koduna sahip bir program örneği (en azından, hakkında uyarılabilir, ancak reddedemez).
Gilles 'SO- kötülük olmayı bırak'

1
Korkarım Lemma’nın ifadesi en iyi yanıltıcı, ona bir yanlışlık tonu var. Kararsızlık, yalnızca (sonsuz) örnek kümeleri terimlerini ifade ederseniz anlamlıdır. (Derleyici yapar her fonksiyon için bir cevap üretmek ve biz her zaman doğru olamayacağını biliyorum ama tek undecidable örneği kapalı var olduğunu söyleyerek.) Lemma ve Kanıtı arasındaki Kişisel paragraf (oldukça Lemma örtüşmeyen bu belirtildiği gibi) bunu düzeltmeye çalışır, ancak açıkça doğru bir lemayı formüle etmenin daha iyi olacağını düşünüyorum.
Raphael

@Raphael Uh? Hayır, derleyicinin “bu işlev sabit mi?” Sorusuna cevap vermesi gerekmez. Çalışma kodu üretmek için “bilmiyorum” ı “hayır” dan ayırt etmesine gerek yok, ancak bu burada geçerli değil çünkü kod derleme bölümünde değil sadece derleyicinin statik analiz kısmına ilgi duyuyoruz. Lemmanın ifadesinde yanıltıcı veya yanlış bulduğunuz şeyi anlamıyorum - amacınız “derleyici” yerine “statik analizör” yazmam gerektiği sürece mi?
Gilles 'SO- kötülük yapmayı bırak'

“Kararsızlık, çözülemeyen bir örnek olduğu” anlamına geliyor, yanlış. (Bunu söylemek istemediğini biliyorum, ama işte o zaman alışılmadıklara / acemilere okuyabilir, yani.)
Raphael

3

jmite'nin cevabı, programın bir hesaplamadan çıkıp çıkmayacağına uygulanır - sadece sonsuz olduğu için, kodun ölülmesinden sonra çağrılmaz.

Bununla birlikte, başka bir yaklaşım daha var: Cevabı olan ancak bilinmeyen bir problem:

public void Demo()
{
  if (Chess.Evaluate(new Chessboard(), int.MaxValue) != 0)
    MessageBox.Show("Chess is unfair!");
  else
    MessageBox.Show("Chess is fair!");
}

public class chess
{
  public Int64 Evaluate(Chessboard Board, int SearchDepth)
  {
  ...
  }
}

Şüphesiz bu rutin gelmez ölü kod içerir - fonksiyon tek yolu ancak diğer değil yürütür bir cevap dönecektir. Yine de bulmakta iyi şanslar! Hafızam teorik bir bilgisayarın bunu evrenin ömrü boyunca çözebileceği bir şey değil.

Daha ayrıntılı olarak:

Evaluate()Her iki taraf da (maksimum arama derinliği) kusursuzca oynarsanız yan bir satranç oyunları kazanır fonksiyon değerlerini hesaplar.

Satranç değerlendiricileri normalde mümkün olan her harekete belirli bir derinlik taşıyarak ileriye bakarlar ve sonra tahtayı bu noktada puanlamaya çalışırlar (bazen bazı dalları bir borsadan veya benzerinden yarıya baktıkça uzatarak çok eğrilmiş bir algı yaratabilir.) Gerçek maksimum derinlikten itibaren 17695 yarım hareket eder arama kapsamlı, olası her satranç oyununu geçecek. Tüm oyunların sona ermesinden dolayı, her bir tahtanın ne kadar iyi bir pozisyon olduğuna karar vermeye çalışmanın bir sorunu yoktur (ve böylece tahta değerlendirme mantığına bakmak için hiçbir sebep yoktur - asla çağrılmaz), sonuç ya bir kazanç, bir kayıp veya bir çizim. Sonuç beraberlik ise oyun adil, sonuç beraberlik değilse bu adil olmayan bir oyundur. Onu biraz genişletmek için:

public Int64 Evaluate(Chessboard Board, int SearchDepth)
{
  foreach (ChessMove Move in Board.GetPossibleMoves())
    {
      Chessboard NewBoard = Board.MakeMove(Move);
      if (NewBoard.Checkmate()) return int.MaxValue;
      if (NewBoard.Draw()) return 0;
      if (SearchDepth == 0) return NewBoard.Score();
      return -Evaluate(NewBoard, SearchDepth - 1);
    }
}

Ayrıca, derleyicinin Chessboard.Score () kodunun ölü kod olduğunu fark etmesi neredeyse imkansız olacak. Satranç kurallarının bilgisi, insanların bunu çözmemize izin verir, ancak bunu çözebilmek için MakeMove'un parça sayısını asla artıramayacağını ve Chessboard.Draw () 'ın parça sayısı çok uzun süre statik kalırsa doğru döneceğini bilmek zorundasınız. .

Arama derinliğinin tüm hareketlerin değil, yarım hareket halinde olduğunu unutmayın. Bu, bu tür AI yordamı için normaldir, çünkü O (x ^ n) yordamıdır - bir tane daha arama katmanı eklemek, ne kadar süreceği üzerinde büyük bir etkiye sahiptir.


8
Bir kontrol algoritmasının hesaplamayı yapmak zorunda kalacağını varsayıyorsunuz. Ortak bir yanlışlık! Hayır, bir kontrolcünün nasıl çalıştığı hakkında hiçbir şey düşünemezsiniz, aksi takdirde varlığını çürütemezsiniz.
Raphael

6
Soru , ölü kodu tespit etmenin imkansız olduğuna dair bir kanıt istiyor . Gönderiniz, ölü kodu tespit etmenin zor olacağından şüphelendiğiniz bir örnek teşkil ediyor. Bu, eldeki sorunun cevabı değil.
David Richerby 11:15

2
@LorenPechtel Bilmiyorum, ama bu bir kanıt değil. Ayrıca buraya bakınız ; Kavram yanılgınızın daha temiz bir örneği.
Raphael

3
Eğer yardım ederse, teorik olarak birisinin derleyicisini evrenin ömründen daha uzun bir süre çalıştırmasına engel olacak bir şey olmadığını düşünün; tek sınırlama pratiklik. Karar verilebilir bir problem, OLMAYAN karmaşıklık sınıfında olsa bile karar verilebilir bir problemdir.
Sahte

4
Başka bir deyişle, bu cevap en iyi ihtimalle tüm ölü kodları tespit eden bir derleyici oluşturmanın neden kolay olmadığını, ancak imkansızlığın bir kanıtı olmadığını göstermek için bir buluşsal yöntemdir. Bu tür bir örnek , öğrenciler için sezgi oluşturmanın bir yolu olarak faydalı olabilir , ancak bu bir kanıt değil. Kendini bir kanıt olarak sunarak, bir kötülük yapar. Cevap, sezginin temelini oluşturduğu ancak imkansızlık kanıtı olmadığını ifade edecek şekilde düzenlenmelidir .
DW

-3

Ben bir hesaplama dersinde, derleme zamanı ile çalışma zamanı arasındaki farkı anlama bağlamında ölü kod kavramının ilginç olduğunu düşünüyorum!

Bir derleyici, hiçbir derleme zamanı senaryosunda hiçbir zaman geçilemeyecek bir kodunuzun olup olmadığını belirleyebilir, ancak çalışma zamanı için bunu yapamaz. Loop-break testi için kullanıcı girişi olan basit bir while-loop bunu göstermektedir.

Bir derleyici çalışma zamanı ölü kodunu gerçekten belirleyebiliyorsa (örneğin, Turing tamamlandı), o zaman işin zaten bitmiş olması nedeniyle kodun çalıştırılması gerekmediği bir argüman var!

Başka bir şey olmazsa, derleme zamanı ölü kod kontrollerini geçen kodun varlığı girdiler ve genel kodlama hijyeninde (gerçek projelerin gerçek dünyasında) pragmatik sınır kontrolünün gerekliliğini göstermektedir.


1
Soru, ölü kodu tespit etmenin mümkün olmadığına dair bir kanıt istiyor. Bu soruyu cevaplamadın.
David Richerby,

Ayrıca, "bir derleyicinin hiçbir zaman derleme zamanı senaryosunda geçilemeyen kodunuz olduğunda ne zaman belirlenebileceğini" belirleyebileceği iddiası yanlıştır ve soruyu kanıtlamanızı istediği şeyle doğrudan çelişir.
David Richerby 12:15

@David Richerby, beni yanlış okuyor olabileceğini düşünüyorum. Derleme zamanı denetimi, TÜM ölü kodu bulabilir, kesinlikle kesinlikle değil. Derleme zamanında farkedilebilen tüm ölü kod kümesinin bir alt kümesi olduğunu öneriyorum. Eğer yazarsam: if (true == false) {print ("bir şey");}, bu print deyimi, derleme anında derleme kodu olarak anlaşılabilir olacaktır. Bunun iddianın bir karşılığı olduğunu kabul etmiyor musun?
dwoz,

Elbette, bazı ölü kodları belirleyebilirsiniz . Ancak, “ne zaman kodun ne zaman olacağını belirlersiniz” diyecek olursanız, o zaman bana göre, sadece bazı kodları değil tüm ölü kodları bulacağım anlamına gelir.
David Richerby 13:15
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.