Teoretycznie wolne od błędów programy


12

Przeczytałem wiele artykułów, w których stwierdzono, że kod nie może być wolny od błędów, i mówią o tych twierdzeniach:

W rzeczywistości twierdzenie Rice'a wygląda jak implikacja problemu zatrzymania, a problem zatrzymania jest ściśle powiązany z twierdzeniem Gödela o niekompletności.

Czy to oznacza, że ​​każdy program będzie miał co najmniej jedno niezamierzone zachowanie? Czy oznacza to, że nie można napisać kodu, aby go zweryfikować? Co z sprawdzaniem rekurencyjnym? Załóżmy, że mam dwa programy. Oba mają błędy, ale nie mają tego samego błędu. Co się stanie, jeśli uruchomię je jednocześnie?

I oczywiście większość dyskusji dotyczyła maszyn Turinga. Co z automatyzacją ograniczoną liniowo (prawdziwe komputery)?


10
Jestem prawie pewien, że ten program python robi wszystko, co powinien, i nie więcej: print "Hello, World!"... czy możesz być trochę bardziej jasny?
durron597

2
@ durron597: Czy istnieje rynek na takie oprogramowanie? Witaj, światowe drukarki, wersja ponownie załadowana? Teraz z więcej cześć i więcej światów?
JensG

1
@Phoshi faugh. Możliwe jest napisanie wystarczająco prostego programu (powiedzmy, za pomocą przewodów na płycie), abyś mógł zobaczyć cały zakres procesu naraz, bez żadnych błędów. Atakujesz szczegóły mojego komentarza, nie odnosząc się do mojej głównej kwestii, a mianowicie, że można pisać wyjątkowo proste programy, które nie zawierają błędów.
durron597

2
@Phoshi „Udowodnij, że Twój interpreter w języku Python nie zawiera błędów”. Błędy w implementacji Pythona nie powodują nieprawidłowego działania programu . Program działa poprawnie, jeśli robi to, co powinien, biorąc pod uwagę, że implementacja języka Python jest zgodna ze specyfikacją języka. Każdy dowód przyjmuje pewne rzeczy jako aksjomaty - na przykład będziesz musiał założyć, że wszechświat będzie istniał przez cały czas wykonywania programu. Jeśli CPython ma błąd, wynik może być nieprawidłowy, ale usterki nie ma w programie.
Doval

11
Żadne z tych twierdzeń nie ma nic wspólnego z błędami lub istnieniem programów wolnych od błędów. Są to twierdzenia o tym, na które pytania można odpowiedzieć obliczeniowo. Twierdzenia te pokazują, że istnieją pewne problemy, których nie można obliczyć, a także niektóre twierdzenia matematyczne, których nie można udowodnić ani obalić, ale z pewnością nie twierdzą, że wszystkie programy zawierają błędy lub że nie można udowodnić, że określone programy są poprawne.
Charles E. Grant

Odpowiedzi:


18

Nie chodzi o to, że programy nie mogą być wolne od błędów; jest to bardzo trudne do udowodnienia, że ​​są, jeśli program, który próbujesz udowodnić, jest nietrywialny.

Pamiętaj, że nie z braku prób. Systemy typów powinny zapewniać pewną pewność; Haskell ma bardzo wyrafinowany system typów, który do pewnego stopnia to robi. Ale nigdy nie można usunąć całej niepewności.

Rozważ następującą funkcję:

int add(int a, int b) { return a + b; }

Co może pójść nie tak z tą funkcją? Wiem już, co myślisz. Powiedzmy, że omówiliśmy wszystkie podstawy, takie jak sprawdzanie przepełnienia itp. Co się stanie, jeśli promień kosmiczny uderzy w procesor, powodując jego wykonanie

LaunchMissiles();

zamiast?

OK, może to trochę wymyślone. Ale nawet proste funkcje, takie jak addpowyższa funkcja, muszą działać w środowiskach, w których procesor stale zmienia konteksty, przełączając się między wieloma wątkami i rdzeniami. W takim środowisku wszystko może się zdarzyć. Jeśli masz co do tego wątpliwości, weź pod uwagę, że pamięć RAM jest niezawodna, nie dlatego, że nie zawiera błędów, ale dlatego, że ma wbudowany system do korygowania błędów bitów, które nieuchronnie występują.

Wiem, o czym myślisz. „Ale mówię o oprogramowaniu, a nie o sprzęcie”.

Istnieje wiele technik, które mogą poprawić poziom pewności, że oprogramowanie działa tak, jak powinno. Programowanie funkcjonalne jest jednym z nich. Programowanie funkcjonalne pozwala lepiej uzasadnić współbieżność. Ale programowanie funkcjonalne nie jest dowodem, podobnie jak testy jednostkowe.

Dlaczego? Ponieważ masz te rzeczy nazywane skrzynkami krawędziowymi.

A kiedy tylko wyjdziesz nieco poza prostotę return a + b, niezwykle trudno jest udowodnić poprawność programu.

Dalsza lektura
Piszą właściwe rzeczy
Wybuch Ariane 5


6
Rozważmy całkowicie poprawną funkcję: int add(int a, int b) { return a - b; }
Donal Fellows

@DonalFellows: Właśnie dlatego zamieściłem link o Ariane 5.
Robert Harvey

2
@DonalFellows - Charakterystyka matematyczna nie rozwiązuje problemu, tylko przenosi go w inne miejsce. Jak udowodnić, że model matematyczny faktycznie reprezentuje potrzebę klienta?
mouviciel

1
@JohnGaughan Zakłada współzależności między modułami. Biorąc pod uwagę moduły, które okazały się poprawne i okazały się od siebie niezależne, możesz rekurencyjnie komponować je w większe moduły, o których wiadomo również, że są poprawne i niezależne ad infinitum.
Doval

1
@JohnGaughan Jeśli integracja modułów powoduje błędy, to nie udowadniasz, że są niezależne. Budowanie nowych dowodów z ustalonych dowodów nie jest trudniejsze niż budowanie dowodów z aksjomatów. Gdyby matematyka stała się wykładniczo trudniejsza w ten sposób, matematycy byliby pokręceni. Możesz mieć błędy w skryptach kompilacji, ale to osobny program. Nie ma elementu tajemniczego, który mógłby się nie udać, gdy próbujesz coś skomponować, ale w zależności od liczby efektów ubocznych może być trudno udowodnić, że nie ma wspólnego stanu.
Doval

12

Najpierw ustalmy, w jakim kontekście chcesz to omówić. Pytania i odpowiedzi programistów w Stack Exchange sugerują, że najprawdopodobniej interesuje Cię istnienie narzędzi / języków w prawdziwym świecie, a nie teoretyczne wyniki i twierdzenia z zakresu informatyki .

Przeczytałem wiele artykułów, w których stwierdzono, że kod nie może być wolny od błędów

Mam nadzieję, że nie, ponieważ takie stwierdzenie jest nieprawidłowe. Chociaż powszechnie przyjmuje się, że zgodnie z moją najlepszą wiedzą i doświadczeniem większość aplikacji na dużą skalę nie jest pozbawiona błędów.

Częściej akceptowane jest to, że nie istnieje (tj. Istnienie, brak możliwości) narzędzie, które doskonale określa, czy program napisany w języku programowania Turing-complete jest całkowicie wolny od błędów.

Non-proof jest intuicyjnym rozszerzeniem problemu zatrzymania, w połączeniu z danymi obserwacyjnymi z codziennych doświadczeń.

Nie robi oprogramowanie może zrobić, że istnieją „dowody poprawności ”, które sprawdzenia, czy program spełnia odpowiednie formalne specyfikacje dla programu.

Czy to oznacza, że ​​każdy program będzie miał co najmniej jedno niezamierzone zachowanie?

Nie. Chociaż większość aplikacji ma co najmniej jeden błąd lub niezamierzone zachowanie.

Czy oznacza to, że nie można napisać kodu, aby go zweryfikować?

Nie, możesz użyć formalnych specyfikacji i asystentów ds. Weryfikacji, aby zweryfikować zgodność ze specyfikacją , ale jak pokazuje doświadczenie, w całym systemie mogą nadal występować błędy, takie jak czynniki spoza specyfikacji - tłumacz kodu źródłowego i sprzęt, a najczęściej popełniane są błędy w samych specyfikacjach.

Aby uzyskać więcej szczegółów, zobacz Coq to takie narzędzie / język / system.

Co z sprawdzaniem rekurencyjnym?

Nie wiem Nie jestem z tym zaznajomiony i nie jestem pewien, czy jest to problem obliczeniowy, czy problem optymalizacji kompilatora.


1
+1 za to, że jako pierwszy mówi o formalnych specyfikacjach i asystentach dowodowych. Jest to kluczowy punkt, którego brakuje w poprzednich odpowiedziach.
Arseni Mourzenko

6

Chcę zapytać, czy to oznacza, że ​​każdy kod uzyska co najmniej jedno niezamierzone zachowanie?

Nie. Właściwe programy mogą być i są napisane. Pamiętaj, że program może być poprawny, ale jego wykonanie może się nie powieść z powodu np. Okoliczności fizycznych (jak napisał użytkownik Robert Harvey w swojej odpowiedzi tutaj ), ale jest to osobna sprawa: kod tego programu jest nadal poprawny. Mówiąc ściślej, awaria nie jest spowodowana błędem lub błędem w samym programie, ale w maszynie, która go wykonuje (*).

(*) Pożyczam definicje błędów , błędów i awarii z pola niezawodności jako, odpowiednio, wady statyczne, niepoprawny stan wewnętrzny i nieprawidłowe obserwowane zachowanie zewnętrzne zgodnie z jego specyfikacją (patrz <dowolny artykuł z tego pola>) .

Czy oznacza to, że nie jestem w stanie napisać kodu, który to sprawdzi?

Sprawdź ogólny przypadek w powyższym stwierdzeniu i masz rację.

Możesz być w stanie napisać program, który sprawdza, czy określony program X jest poprawny. Na przykład, jeśli zdefiniujesz program „hello world” jako jeden z dwiema instrukcjami w kolejności, a mianowicie print("hello world")i exitmożesz napisać program, który sprawdza, czy jego dane wejściowe to program złożony z tych dwóch instrukcji w sekwencji, raportując, czy jest to poprawny program „witaj świecie”, czy nie.

Tym, czego nie można zrobić przy użyciu bieżących sformułowań, jest napisanie programu, aby sprawdzić, czy dowolny program się zatrzymuje, co implikuje niemożność sprawdzenia poprawności w ogólnych przypadkach.


4

Uruchamianie dwóch lub więcej wariantów tego samego programu jest dobrze znaną techniką tolerancji błędów zwaną programowaniem w wariancie N (lub wersji N). Jest to potwierdzenie obecności błędów w oprogramowaniu.

Zazwyczaj te warianty są kodowane przez różne zespoły programistów, przy użyciu różnych kompilatorów, a czasami są wykonywane na różnych procesorach z różnymi systemami operacyjnymi. Wyniki głosowane są przed wysłaniem do użytkownika. Boeing i Airbus uwielbiają tego rodzaju architekturę.

Pozostają dwa słabe linki prowadzące do błędów w trybie wspólnym:

  • Jest tylko jedna specyfikacja.
  • system głosowania jest unikalny lub złożony.

5
Wierzę, że NASA lub inny program kosmiczny zasugerował, że wariant N cierpi na problem, który zbyt często programiści myślą podobnie i w rezultacie samodzielnie piszą prawie równoważne programy ze zwykłymi wadami, gdy wada jest na poziomie najbardziej trywialnym. Na przykład odwołaj się do tych samych informacji referencyjnych (zobacz długotrwały błąd w wyszukiwaniu binarnym ), zwykle używaj tych samych algorytmów i popełniaj te same rodzaje błędów.
mctylr

@mctylr - To bardzo dobra uwaga. Ale tak naprawdę do niedawna w pamięci brakowało miejsca do przechowywania więcej niż jednego wariantu oprogramowania na statku kosmicznym. Ich odpowiedzią jest test, test, test, płukanie, powtórzenie.
mouviciel

Program promu kosmicznego użył trzech niezależnych konfiguracji głosowania w systemie. Wszelkie odrębne głosy oznaczały (lub, naprawdę, sugerowały), że system nie jest już poprawny i został wyłączony.

4

Program ma pewną specyfikację i działa w pewnym środowisku.

(przykład promienia kosmicznego w innych odpowiedziach zmieniających się addna FireMissiles można uznać za część „środowiska”)

Zakładając, że możesz formalnie określić zamierzone zachowanie programu (tj. Jego specyfikację) i jego środowisko, możesz czasem formalnie udowodnić, że program jest - w tym precyzyjnym sensie - wolny od błędów (więc jego zachowanie lub dane wyjściowe są zgodne z formalizacją specyfikacji w formalizacji jego środowiska).

W szczególności możesz użyć dźwiękowych analizatorów źródeł statycznych, takich jak np. Frama-C .

(ale obecny stan takich analizatorów nie pozwala na analizę całego programu i sprawdzenie programów na dużą skalę, takich jak kompilator GCC, przeglądarka Firefox lub jądro Linuksa; i wierzę, że takie dowody nie pojawią się za mojego życia Urodziłem się w 1959 r.)

Jednak to, co udowodniłeś, to prawidłowe zachowanie programu w określonej specyfikacji w niektórych (klasach) środowiskach.

Praktycznie rzecz biorąc, możesz (i NASA lub ESA prawdopodobnie tego chcą) udowodnić, że niektóre oprogramowanie statków kosmicznych jest „wolne od błędów” i zawiera precyzyjną i sformalizowaną specyfikację. Ale to nie znaczy, że Twój system będzie zawsze zachowywał się tak, jak chcesz.

Mówiąc prościej, jeśli twój robot kosmiczny spełnia jakieś ET i nie określiłeś tego, nie ma sposobu, aby twój robot zachowywał się tak, jak naprawdę chcesz ...

Przeczytaj także wpisy na blogu J.Pitrat .

BTW, problem Haltinga lub twierdzenie Gödla mają prawdopodobnie zastosowanie również do ludzkiego mózgu, a nawet gatunku ludzkiego.


Być może lepszym przykładem SEU zmieniając wezwanie do Addcelu LaunchMissilesbyłby SEU zmieniając pewne wartości danych, który ostatecznie prowadzi do błędnego wywołania LaunchMissiles. SEU to problem z komputerami, które lecą w kosmos. Dlatego współczesne statki kosmiczne często latają na wielu komputerach. Dodaje to nowy zestaw problemów, zarządzania współbieżnością i redundancją.
David Hammen,

3

Czy to oznacza, że ​​każdy program będzie miał co najmniej jedno niezamierzone zachowanie?

Nie.

Problem zatrzymania mówi, że nie można napisać programu, który sprawdza, czy każdy program zatrzymuje się w określonym czasie. Nie oznacza to, że niemożliwe jest napisanie programu, który może zakwalifikować niektóre programy jako wstrzymujące, a inne jako nie zatrzymujące. Oznacza to, że zawsze będą istnieć pewne programy, których analizator zatrzymujący nie może sklasyfikować w taki czy inny sposób.

Twierdzenia Gödela o niekompletności mają podobny szary obszar do nich. Biorąc pod uwagę matematyczny system o wystarczającej złożoności, będą istnieć pewne stwierdzenia złożone w kontekście tego systemu, których prawdziwości nie można ocenić. Nie oznacza to, że matematycy muszą zrezygnować z idei dowodu. Dowód pozostaje kamieniem węgielnym matematyki.

Niektóre programy można udowodnić, że są poprawne. To nie jest łatwe, ale można to zrobić. Taki jest cel formalnego dowodzenia twierdzeń (część metod formalnych). Uderzają tu twierdzenia Gödela o niekompletności: Nie wszystkie programy można udowodnić, że są poprawne. Nie oznacza to, że stosowanie metod formalnych jest całkowicie bezcelowe, ponieważ niektóre programy rzeczywiście można formalnie udowodnić, że są poprawne.

Uwaga: Metody formalne wykluczają możliwość uderzenia promienia kosmicznego w procesor i wykonania launch_missiles()zamiast niego a+b. Analizują programy w kontekście abstrakcyjnej maszyny, a nie prawdziwych maszyn, które podlegają zdenerwowaniu pojedynczym zdarzeniem, takim jak promień kosmiczny Roberta Harveya.


1

Istnieje tutaj wiele dobrych odpowiedzi, ale wszystkie wydają się omijać punkt krytyczny, a mianowicie: wszystkie te twierdzenia mają podobną strukturę i mówią podobne rzeczy, a to, co mówią, nie jest „prawdopodobnie niemożliwe jest poprawne napisanie programy”(dla niektórych wartości określonej w«poprawne»i«program», który zmienia się w każdym przypadku), ale co zrobić, powiedzieć,«że to niemożliwe, aby zapobiec ktoś pisze błędny program, który nie może okazać się niewłaściwe»( itp).

Biorąc pod uwagę konkretny przykład problemu zatrzymania, różnica staje się wyraźniejsza: oczywiście istnieją programy, które można zatrzymać, a inne programy, które prawdopodobnie nigdy się nie zatrzymają. To, że istnieje trzecia klasa programów, których zachowania nie można ustalić w żaden sposób, nie stanowi problemu, jeśli wszystko, co chcemy zrobić, to napisać program, który da się zatrzymać, ponieważ możemy po prostu uniknąć pisania programu, który należy do tej klasy.

To samo dotyczy twierdzenia Rice'a. Tak, dla każdej nietrywialnej własności programu możemy napisać program, w którym ta właściwość nie będzie ani sprawdzona, ani prawdziwa, ani fałszywa; możemy również uniknąć pisania takiego programu, ponieważ jesteśmy w stanie ustalić, czy program jest możliwy do udowodnienia.


0

Moja odpowiedź będzie z perspektywy realnego biznesu i wyzwań, przed którymi stoi każdy zespół programistów. To, co widzę w tym pytaniu i wiele odpowiedzi, naprawdę dotyczy kontrolowania wad.

Kod może być wolny od błędów. Pobierz dowolny przykładowy kod „Hello World” dla dowolnego języka programowania i uruchom go na platformie, na której jest przeznaczony, i będzie działał konsekwentnie i dawał pożądane wyniki. Kończą się teorie na temat niemożności, by kod był wolny od błędów.

Potencjalne błędy pojawiają się, gdy logika staje się bardziej złożona. Prosty przykład Hello World nie ma logiki i za każdym razem robi to samo. Natychmiast po dodaniu dynamicznego działania opartego na logice wprowadza się złożoność, która prowadzi do błędów. Sama logika może być wadliwa lub dane wprowadzane do logiki mogą się różnić w sposób, w jaki logika nie obsługuje.

Nowoczesna aplikacja zależy również od bibliotek wykonawczych, CLR, oprogramowania pośredniego, bazy danych itp., Które - choć ogólnie oszczędzają czas programowania - są również warstwami, w których mogą występować błędy w tych warstwach, które nie są wykrywane podczas testowania i testowania UAT i do produkcji.

Wreszcie, łańcuch aplikacji / systemów, w których aplikacja zużywa dane, które zasilają jej logikę, to wszystkie źródła potencjalnych błędów w ich logice lub w oprogramowaniu, które układa logikę na wierzchu lub w systemach, w których pobiera dane.

Deweloperzy nie mają 100% kontroli nad każdym ruchomym elementem wspierającym logikę ich aplikacji. W rzeczywistości nie kontrolujemy wiele. Dlatego testowanie jednostkowe jest ważne, a zarządzanie konfiguracją i zmianami to ważne procesy, których nie możemy ignorować ani być leniwi / niedbali.

Udokumentowane umowy między aplikacją, która zużywa dane ze źródła, na które nie masz wpływu, które określają konkretny format i specyfikacje przesyłanych danych, a także wszelkie ograniczenia lub ograniczenia, które system przyjmuje, że system źródłowy jest odpowiedzialny za zapewnienie, że dane wyjściowe mieszczą się w zakresie te granice.

W rzeczywistej aplikacji inżynierii oprogramowania nie będziesz w stanie sprawić, by latała, wyjaśniając biznesowi, dlaczego teoretycznie aplikacje nie mogą być wolne od błędów. Dyskusje na ten temat między technologią a biznesem nigdy nie będą się odbywać, chyba że w następstwie awarii technologicznej, która wpłynęła na zdolność firmy do zarabiania pieniędzy, zapobiegania utracie pieniędzy i / lub utrzymywania ludzi przy życiu. Odpowiedź na „jak to się może zdarzyć” nie może być „pozwólcie mi wyjaśnić tę teorię, abyście rozumieli”.

Pod względem ogromnych obliczeń, które teoretycznie mogą trwać wiecznie, aby wykonać obliczenia i uzyskać wynik, aplikacja, która nie może zakończyć i powrócić z wynikiem - to jest błąd. Jeśli charakter obliczeń jest taki, że jest bardzo czasochłonny i intensywny obliczeniowo, przyjmujesz to żądanie i przekazujesz użytkownikowi informacje zwrotne, w jaki sposób / kiedy może on uzyskać wynik, i uruchamiasz równoległe wątki, aby się na nim zgubić. Jeśli musi to nastąpić szybciej, niż można to zrobić na jednym serwerze i jest to wystarczająco ważne z biznesowego punktu widzenia, należy skalować go na tyle systemów, ile potrzeba. Właśnie dlatego chmura jest bardzo atrakcyjna, a możliwość spinania węzłów w celu podjęcia pracy i spinania ich po zakończeniu.

Jeśli istnieje możliwość otrzymania żądania, że ​​żadna ilość mocy obliczeniowej nie jest w stanie ukończyć, nie powinna spędzać czasu w nieskończoności z procesem biznesowym czekającym na odpowiedź na pytanie, co według firmy jest skończonym problemem.


-2

Nie wierzę, że kod jest w 100% wolny od błędów, ponieważ kod nigdy nie jest naprawdę gotowy. Zawsze możesz poprawić to, co piszesz.

Programowanie jest dziedziną nauki i matematyki, w którym to przypadku oba są nieograniczone. Wspaniałą rzeczą w byciu programistą jest to, że nasza praca jest nieograniczona.

Istnieje ponad tysiąc sposobów na napisanie jednego wiersza kodu. Chodzi o to, aby napisać najbardziej zoptymalizowaną wersję tego wiersza kodu, ale może to nie być wolne od błędów. Bezbłędny odnosi się do idei, że Twój kod jest niezniszczalny, a cały kod można złamać w pewnym stopniu lub w dowolnej metodzie.

Czy kod może być wydajny? Tak.
Czy kod można optymalizować bez końca? Tak.
Czy kod może być wolny od błędów? Nie, po prostu nie znalazłeś jeszcze sposobu, aby go złamać.
Biorąc to pod uwagę, jeśli starasz się poprawić siebie i swoje praktyki pisania kodu, Twój kod będzie trudny do złamania.


ten post jest raczej trudny do odczytania (ściana tekstu). Czy mógłbyś edytować go w lepszym kształcie?
komara
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.