In einer Nussschale:
Teilrichtigkeit ist eine Frage der Kündigung, nicht der Richtigkeit der berechneten Daten. Eine Funktion ist in Bezug auf eine Spezifikation teilweise korrekt, wenn alles, was sie berechnet, korrekt ist, wenn sie beendet wird. Diese Idee kann auf die Berechnung unvollständiger (Teil-) Antworten ausgedehnt werden. Was auch immer aus der Antwort berechnet wird, ist korrekt, aber das Programm kann irgendwann in eine nicht terminierende Schleife geraten, möglicherweise ohne die gesamte Antwort berechnet zu haben. Teilantworten sind Annäherungen an vollständige Antworten.
Diese Approximationsstruktur ist eine Teilordnung, die das Grundkonzept von Scotts Semantikdomänen darstellt und tatsächlich zur Beantwortung einer anderen Interpretation der Frage verwendet werden kann. Können wir einen Abstand zwischen einer richtigen und einer nicht ganz richtigen Antwort messen, z. B. wenn ein Element eines Arrays falsch (und nicht unbekannt) ist? Eine Möglichkeit, einen solchen Abstand zu definieren, besteht darin, die Näherungsreihenfolge zu berücksichtigen und die beiden inkompatiblen Antworten (die richtige und die falsche) mit der besten Teilantwort zu verknüpfen, die eine Annäherung an beide ist. Dieses Problem wird unter dem Gesichtspunkt der numerischen Analyse, bei der eine Präzisionsanalyse unerlässlich ist, und einiger anderer Bereiche schnell betrachtet.
Dieser zweite Punkt wird tatsächlich in einer zweiten Antwort auf die Frage untersucht, da mir zunächst nicht klar war, dass die beiden Antworten einen Zusammenhang haben könnten. Aber beide Antworten sind ziemlich lang, und ich fühlte mich nicht klug, sie zusammenzuführen, als ich die Verbindung erkannte.
Eine erste einfache Ansicht der teilweisen Korrektheit
Es gibt keine Möglichkeit, im absoluten Sinne (teilweise) korrekt zu sein . Ein Programm ist korrekt, wenn es einer bestimmten Spezifikation entspricht. Die Spezifikation kann ein anderes Programm oder eine logische Anweisung sein oder was auch immer formalisiert werden kann. Die Spezifikation muss irgendwie Informationen darüber enthalten, wann das Programm beendet wird, möglicherweise immer (was in den meisten Definitionen tatsächlich angenommen wird, so dass nichts Komplexeres gesagt werden muss). Tatsächlich kann die Domain in der Spezifikation auf den Teil beschränkt sein, in dem eine Kündigung erwartet wird, so dass immer eine Kündigung erwartet wird , was die Annahme einer Kündigung in der gesamten Domäne in der üblichen Definition ( Wikipedia und Wikipedia) rechtfertigen kann). Dies setzt wiederum eine implizite Annahme für jede Spezifikation voraus, die so bequem sein kann oder nicht.
Ein Programm ist in Bezug auf eine Spezifikation korrekt,
wenn es beendet wird, wann immer die Spezifikation dies vorschreibt, und mit einem Ergebnis, das der Spezifikation entspricht. Es ist teilweise richtig, wenn es manchmal nicht dort endet, wo es in der Spezifikation angegeben ist, aber beim Beenden immer ein korrektes Ergebnis liefert.PS
Infolgedessen ist ein Programm, das niemals beendet wird, in Bezug auf eine Spezifikation teilweise korrekt .
Ich habe eine leicht erweiterte Definition gewählt, auch weil sie genau dem Begriff der Approximation in Scotts semantischen Domänen entspricht , wie er in der Denotationssemantik verwendet wird. Eine Scott-Domäne enthält eine Teilreihenfolge, die genau der Idee der Teilkorrektheit entspricht (die beiden Verwendungen des Wortes "Teil" sind etwas unabhängig voneinander). Eine Funktion ist und die Approximation einer Funktion ist endet immer dann, wenn
endet, und beide ergeben dann das gleiche Ergebnis. So kann ein Ergebnis geben , wenn nicht. Und wir können sagen, dass Bezug auf teilweise richtig ist oder dassFGGFGFFGFapproximieren oder .GF⊑G
Diese Ideen sind wichtig, um die Semantik von Funktionen mit Schleife (oder Rekursion) als die Grenze eines unendlichen Satzes von Funktionen ohne Schleife oder Rekursion zu definieren. Siehe zum Beispiel Wikipedia oder eine sehr informelle Präsentation über SE .
Die Standard-Hoare-Logik funktioniert nur zum Nachweis der teilweisen Korrektheit und muss erweitert werden, um die Terminierungseigenschaften und damit die vollständige Korrektheit zu adressieren (siehe Wikipedia ). Es gibt Beispiele für solche spezifischen Erweiterungen.
Der Nachweis der vollständigen Korrektheit bedeutet den Nachweis der teilweisen Korrektheit und Beendigung. Die Hoare-Logik ist für die teilweise Korrektheit durchaus geeignet. Der Nachweis der Beendigung erfordert normalerweise einen Beweis durch Induktion (Wiederholung), was der natürliche Ansatz ist, um Dinge in Scotts Semantik zu beweisen (da die Semantik selbst auf diese Weise induktiv definiert ist). Die Antwort von Danielp zeigt, wie eine solche Induktion einen Beweis in Hoares Logik ergänzen kann.
Wenn Sie die teilweise Korrektheit quantifizieren möchten, können Sie die Teile der Domäne, in denen das Programm beendet wird oder nicht, oder einige Eigenschaften dieser Teile identifizieren, sofern Sie dies dennoch möchten.
Erweiterung auf komplexe Ergebnisse, angewendet auf das Sortierbeispiel.
Tatsächlich kann das Problem etwas komplexer sein, wenn Sie komplexe Antworten wie Datenstrukturen berücksichtigen (was beim Sortieren von Arrays der Fall ist). Die Spezifikation könnte die Berechnung von zwei Antworten (dh eines Paares) erfordern, und für einige Teile der Eingabedomäne könnte ein tatsächliches Programm ein Element des Paares finden, aber nicht enden, während das andere berechnet wird, in anderen Fällen nur das andere Element, oder finden Sie beide, oder finden Sie keine. Dies ist immer noch eine Annäherung im Sinne von Scott, und ein solches Programm ist teilweise korrekt.
Allgemeiner gilt die Idee der Annäherung im Scott-Sinne sowohl für Daten als auch für Programme. Dazu benötigen Sie informell das Konzept einer unbekannten Antwort (noch nicht berechnet, möglicherweise nie bekannt, wenn die Berechnung nicht beendet wird). Es wird normalerweise durch das Symbol . Das Paar nähert sich . Was Sie in einem Programm erhalten, das den 36-Teil liefert und dann nicht beendet wird, kann durch .⊥(⊥,36)(25,36)(⊥,36)
Wie kann dies auf ein Programm angewendet werden, das Arrays mit fünf Ganzzahlen sortiert? Angenommen, Sie schreiben ein Programm SORT5, das parallel zu Ihrer Hauptanwendung ausgeführt wird (ich versuche, die Dinge realistisch zu machen) und ein solches Array für die Anwendung sortieren soll. Das Programm SORT5 soll sein Ergebnis in einem von der Anwendung bereitgestellten Array speichern und kann dies für jedes Element separat tun, sobald es weiß, wo es platziert werden soll. Es sucht zuerst nach dem größten und dem kleinsten und speichert sie an beiden Enden. Dann versucht es, eine Rekursion (oder was auch immer) durchzuführen, hat jedoch einen Fehler, der es ohne weitere Ergebnisse in eine Endlosschleife sendet. Die Hauptanwendung erhält noch eine teilweise Antwort. Wenn das zu sortierende Array , lautet die Antwort[25,36,3,9,12][3,⊥,⊥,⊥,36]anstelle von
. Was auch immer bereitgestellt wird, ist korrekt und der Rest wird nicht berechnet, sodass Sie nur einen Teil der Antwort haben . Sie haben also eine Annäherung an das gewünschte Ergebnis. Wenn Sie nachweisen können, dass dies immer der Fall ist, ist Ihr fehlerhaftes Programm SORT5, das nicht beendet wird, in Bezug auf die Spezifikation eines Sortierprogramms teilweise noch korrekt.[3,9,12,25,36]
Ein teilweise korrektes Programm kann nützlich sein. Es könnte sein, dass Sie nicht wirklich sortieren mussten, sondern nur das größte und kleinste Element. In diesem Fall spielt die Tatsache, dass Ihr Sortierprogramm SORT5 nicht beendet wird und nur teilweise korrekt ist, keine Rolle, und Ihre Anwendung funktioniert und wird hoffentlich mit einer korrekten Antwort beendet.
Aber wer wird Ihren Rogue-Sortieralgorithmus stoppen, der weiterhin Rechenleistung verschwendet? Es gibt Berechnungsstrategien (Lazy Evaluation), bei denen kein Unterprogramm ausgeführt wird, wenn derzeit keine weiteren Informationen zum Ergebnis benötigt werden. Nachdem Sie das größte und kleinste Element erhalten haben, wird das Programm SORT5 angehalten, bis andere Elemente angefordert werden.
In diesem Fall könnte es natürlich eine Möglichkeit geben, die teilweise Korrektheit zu quantifizieren. Ich bin mir jedoch nicht sicher, ob es sehr nützlich wäre.
In diesem Zusammenhang ist es notwendig, die Definition ein wenig zu überarbeiten, was ich etwas informell mache:
Ein Programm P ist in Bezug auf eine Spezifikation S teilweise korrekt, wenn es eine vollständige Antwort gibt, die der Spezifikation vor dem Beenden entspricht, oder einen Teil einer Antwort liefert, die der Spezifikation entspricht, bevor eine nicht terminierende Berechnung durchgeführt wird, die keinen weiteren Teil der Antwort liefert .
Dann ist ein Programm, das niemals beendet wird und keinen Teil des Ergebnisses erzeugt, in Bezug auf eine Spezifikation teilweise korrekt.
Beachten Sie, dass diese Definition ein Programm auslässt, das ständig rechnet und immer neue Teile der Antwort erzeugt. Aber da es keine Infinitesimale erzeugt (ich weiß nicht, dass dies rechnerisch sinnvoll sein könnte), berechnet es tatsächlich eine unendliche Antwort.
Diese Techniken können tatsächlich sehr fruchtbar sein, um die Semantik der Berechnung eines unendlichen Objekts (nur für sehr geduldige Benutzer) zu formalisieren, wie beispielsweise die exakte dezimale (oder binäre) Darstellung des Werts von oder unendliche Listen. Es gibt andere interessante Anwendungen. Aber das ist weit von der ursprünglichen Frage entfernt, und deshalb lasse ich es weg.π