Eine unberührte Welt

16

Diese Herausforderung basiert auf Helka Hombas Frage Programmieren einer unberührten Welt . Aus dieser Frage heraus lautet die Definition eines makellosen Programms:

Definieren wir ein unberührtes Programm als ein Programm, das selbst keine Fehler aufweist, das jedoch fehlerhaft ist, wenn Sie es ändern, indem Sie zusammenhängende Teilzeichenfolgen mit N Zeichen entfernen 1 <= N < program length.

Zum Beispiel das dreistellige Python 2-Programm

`8`

ist ein unberührtes Programm ( danke, Sp ), da alle Programme, die aus dem Entfernen von Teilzeichenfolgen der Länge 1 resultieren, Fehler verursachen (Syntaxfehler in der Tat, aber jede Art von Fehler reicht aus):

8`
``
`8

und auch alle Programme, die aus dem Entfernen von Teilzeichenfolgen der Länge 2 resultieren, verursachen Fehler:

`
`

Wenn zum Beispiel `8ein fehlerfreies Programm gewesen `8`wäre, wäre es nicht makellos, da alle Ergebnisse der Entfernung der Teilzeichenfolgen fehlerhaft sein müssen.

Anmerkungen:

  • Compiler-Warnungen gelten nicht als Fehler.
  • Die fehlerhaften Unterprogramme können Eingaben oder Ausgaben annehmen oder irgendetwas anderes tun, solange sie fehlerhaft sind, egal was letztendlich passiert.

Ihre Aufgabe ist es, ein Programm mit einer Länge ungleich Null zu erstellen, das seinen eigenen Quellcode genau ausgibt und dem folgt ausgibt , Regeln für ein korrektes Quine und makellos ist.

Die kürzeste Antwort in Bytes für jede Sprache gewinnt.

Shelvacu
quelle
Ich gehe davon aus, dass fehlerfreie Sprachen nicht mithalten können.
ATaco
@ATaco Leider ja. Andere Sprachen wie lisp haben die Syntax so strukturiert, dass es unmöglich ist, ein nützliches, makelloses Programm zu erstellen.
Shelvacu
RIP Eigentlich / Ernsthaft
ATaco
"Kürzeste Antwort in Bytes für jede Sprache gewinnt." Ich bin mir nicht sicher, ob Short das beste Maß für ein makelloses Programm ist.
P. Siehr
@ P.Siehr Was würdest du stattdessen empfehlen?
Shelvacu

Antworten:

6

Haskell , 132 Bytes

q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"

Probieren Sie es online!

Dies ist eine Erweiterung des Quines

main=putStr$(++)<*>show$"main=putStr$(++)<*>show$"

showDies funktioniert, indem der Datenstring mit einer zitierten Version (unter Verwendung von ) von sich selbst verknüpft und das Ergebnis gedruckt wird. Dies ist jedoch nicht makellos, da alle Zeichen im Datenstring ohne Fehler entfernt werden können und auch das $(++)<*>show$oder(++)<*> Teil gelöscht werden kann, ohne dass das Programm unterbrochen wird.

Um dies zu beheben, wird eine benutzerdefinierte Druckfunktion qdefiniert, die die Länge der angegebenen Zeichenfolge überprüft und aufruft, failob sie kürzer als 132 ist. Dadurch wird das Entfernen einer beliebigen Sequenz aus der Datenzeichenfolge und auch das Entfernen von $(++)<*>show$oder (++)<*>, wie in beiden Fällen das Ergebnis, abgefangen Zeichenfolge, an die übergeben wird, qist kürzer.

In qder Nummer 132könnte gekürzt 1werden 13, 32oder 2, aber in jedem Fall wird nochmal failangerufen.

Soweit ich das beurteilen kann, führt das Entfernen eines anderen Teilstrings entweder zu einem Syntax- oder zu einem Typfehler, sodass das Programm überhaupt nicht kompiliert. (Haskells strenges Typensystem ist hier nützlich.)

Edit: Vielen Dank an Ørjan Johansen und Shelvacu für den Hinweis auf einen Fehler!

Laikoni
quelle
Ich fürchte, fail[]|length x/=122kann entfernt werden. fail[]:[putStr x|length x==122]könnte besser funktionieren.
Ørjan Johansen
Argh, nein, |length x==122könnte dann entfernt werden. if length x==122 then putStr x else fail[]vielleicht?
Ørjan Johansen
@ ØrjanJohansen Guter Fang, ich hatte einen if then elsevor, dachte aber, ich könnte ihn verkürzen.
Laikoni
2
putStr xKann werden p x, was, als ich mein System anprobierte, sehr lange lief, bevor ich es abbrach, ich vermute, dass die Endanruf-Rekursion optimiert wurde, so dass es eine Endlosschleife ist. Ich kenne nicht genug Haskell, um Vorschläge zu machen, wie das Problem behoben werden kann.
Shelvacu
@Shelvacu Whoops. Das Umbenennen pin qsollte das beheben.
Ørjan Johansen
4

Python 3 , 113 Bytes

for[]in{113:[]}[open(1,"w").write((lambda s:s%s)('for[]in{113:[]}[open(1,"w").write((lambda s:s%%s)(%r))]:a'))]:a

Probieren Sie es online!

Wie es funktioniert

Es ist nicht einfach, mehrere Anweisungen zu verwenden, da die zweite gelöscht werden kann. Daher beginnen wir mit einem einfachen Ausdruck:

print((lambda s:s%s)('print((lambda s:s%%s)(%r))'))

Zum Schutz vor Löschungen von Teilzeichenfolgen verwenden wir open(1,"w").writeanstelle vonprint . Gibt in Python 3 writedie Anzahl der geschriebenen Zeichen zurück, um 113sicherzustellen, dass kein Teil der Zeichenfolge gelöscht wurde. Wir tun dies, indem wir den Rückgabewert im Dictionary {113:[]}nachschlagen und das Ergebnis mit for[]in…:adurchlaufen, was fehlschlägt, wenn wir kein leeres Iterable erhalten haben oder wenn die forAnweisung gelöscht wird.

Anders Kaseorg
quelle
1
Können Sie erklären, wie Ihr Code funktioniert?
Shelvacu
@Shelvacu Ja, hinzugefügt.
Anders Kaseorg
3

Ruby, 78 Bytes

eval(*[($>.write((s=%{eval(*[($>.write((s=%%{%s})%%s)-78).chr])})%s)-78).chr])

Ich schrieb dies, als ich über die Herausforderung nachdachte, um sicherzustellen, dass es möglich war. Es verwendet den gleichen "Wrapper" aus einer meiner Antworten auf die ursprüngliche Herausforderung verwendet.

Erläuterung:

  • eval(*[ ausdr ])

    Dies wertet den Code aus, der als Ruby-Programm zurückgegeben wird. Dies testet effektiv, dass die Zeichenfolge Code zurückgegebene ein gültiges Ruby-Programm ist. Praktischerweise können Ruby-Programme leer sein oder nur aus Leerzeichen bestehen.

    Mit dem Operator "splat" *können Sie ein Array als Argumente für eine Funktion verwenden. Dies bedeutet auch, dass, wenn evalentfernt, das resultierende Programm (*[ expr ist ]) , was kein gültiger Ruby ist.

  • ($>.write( str )-78).chr

    $> ist eine kurze Variable für STDOUT.

    $>.write(foo) schreibt foo nach STDOUT und gibt, was für diesen Code wichtig ist, die Anzahl der geschriebenen Bytes zurück.

    $>.write(foo)-78: Hier 78ist die Länge des Programms, und wenn das Programm nicht entstellt ist, wird auch die Anzahl der geschriebenen Bytes angegeben. Aus diesem Grund wird im ungemischten Fall Null zurückgegeben.

    num.chrGibt num als Zeichen zurück, z. B. 0.chrwird ein String mit einem einzelnen Null-Byte zurückgegeben. In dem nicht vermischten Programm gibt dies eine Zeichenkette mit einem einzelnen Null-Byte an eval, was ein gültiges Ruby-Programm ist, das eine No-Op ist.

    Im Programm kann auch eine Teilzeichenfolge entfernt werden, sodass es nur eval(*[(78).chr])oder isteval(*[(8).chr]) , was bedeutet, dass die numerische Konstante nicht mit einer der Zahlen (0, 4, 9, 10, 11, 12, 13, 26, 32, 35, 48) enden kann , 49, 50, 51, 52, 53, 54, 55, 56, 57, 59, 64, 95), weil sie ASCII-Codes für gültige Ruby-Programme mit einem Zeichen sind.

  • %{ str }

    Dies ist eine weniger bekannte Syntax für String-Literale in Ruby. Der Grund, warum es hier verwendet wird, ist, dass ausgeglichene Paare von {}innerhalb des Strings verwendet werden können, was bedeutet, dass diese Syntax sich selbst enthalten kann. Zum Beispiel %{foo{bar}}ist das gleiche wie "foo{bar}".

  • (s=%{ Daten })%s

    Dies definiert die Variable s die die Daten dieses Quines sind, als printf-String.

    Zuweisungen in Ruby geben das zurück, was zugewiesen wurde. Dies entspricht also dem Zuweisen sund anschließenden Ausführens%s

    %an einer Schnur ist syntatischer Zucker für Ruby-Äquivalent von Sprintf. Das%s bedeutet, wo in den Daten die Daten selbst eingebettet werden sollen.

    Dieses Codebit definiert den Datenteil des Quines und bettet ihn in sich ein, um den vollständigen Code zu erstellen.

Shelvacu
quelle
3

Standard ML (MLton) , 204 182 189 Bytes

val()=hd[(fn s=>let val$ =s^"\""^String.toString s^"\"]"val(189,%)=(size$,$)in print%end)"val()=hd[(fn s=>let val$ =s^\"\\\"\"^String.toString s^\"\\\")\"val(189,%)=(size$,$)in print%end)"]

Probieren Sie es online!

Für MLton sind vollständige SML-Programme entweder Ausdrücke, die durch ;(z. B. print"Hello";print"World";) begrenzt und abgeschlossen werden, oder Deklarationen mit den Schlüsselwörtern varund fun(z. B. var _=print"Hello"var _=print"World"), wobei _es sich um einen Platzhalter handelt, der auch durch einen beliebigen Variablennamen ersetzt werden kann.

Die erste Option ist für makellose Programmierung nutzlos, da es sich ;für sich genommen um ein gültiges Programm handelt (das nichts tut, aber auch keinen Fehler macht). Das Problem mit dem zweiten Ansatz ist, dass Deklarationen wie var _=print"Hello"nur var _="Hello"(oder sogar gekürzt werden könnenvar _=print ) weil die Deklaration mitvar funktioniert, solange die rechte Seite ein gültiger SML-Ausdruck oder -Wert ist (SML ist eine funktionale Sprache, also können Funktionen sein) auch als Werte verwendet).

Zu diesem Zeitpunkt war ich bereit, makellose Programmierung in SML für unmöglich zu erklären, als ich zufällig auf Pattern Matching in val-Deklarationen stieß. Es stellt sich heraus, dass die Syntax für Deklarationen nicht val <variable_name> = <expression>aber so ist val <pattern> = <expression>, dass ein Muster aus Variablennamen, Konstanten und Konstruktoren bestehen kann. Da der printFunktionstyp hat string -> unit, können wir eine Mustererkennung auf der Verwendung unit-Wertes ()zu erzwingen , dass die Druckfunktion tatsächlich auf den String angewandt wird: val()=print"Hey". Bei diesem Ansatz führt das Entfernen von entweder printoder "Hey"zu einem Pattern and expression disagreeFehler.

Mit dieser Art des makellosen Druckens besteht der nächste Schritt darin, eine Quine zu schreiben, bevor schließlich weitere Sicherheitsmaßnahmen hinzugefügt werden müssen. Früher habe ich bisher eine einfache SML quine Technik (die sehen Revisionsgeschichte ), aber Anders Kaseorg wies einen anderen Ansatz aus , der einige Bytes in seinem Fall speichern. Es verwendet die eingebaute String.toStringFunktion, um das Entziehen von Zeichenfolgen zu handhaben, und hat die allgemeine Form <code>"<data>", wobei "<data>"es sich um eine entkoppelte Zeichenfolge der codeVorgängerversion handelt:

val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"

Dies ist eine funktionierende Quine, aber noch nicht makellos. Zunächst stellte Anders Kaseorg fest, dass MLton ein einfaches Anführungszeichen "als Code akzeptiert, ohne Fehler zu erzeugen. Dies bedeutet, dass der Code nicht wie oben in einem Anführungszeichen endet. Der kürzeste Weg, dies zu verhindern, wäre, alles nachher val()=in Klammern zu setzen, aber dann könnte der Code auf reduziert werden val()=(). Der zweitkürzeste Weg, den ich gefunden habe, besteht darin val()=hd[ ... ], alles in eine Liste zu packen und das erste Element zurückzugeben, um den Typprüfer glücklich zu machen.

Damit kein Teil des Datenstrings unbemerkt entfernt werden kann, bietet sich die Mustererkennung in val-declarations wieder an: Die Länge des endgültig zu druckenden Strings (und damit die Programmlänge) sollte also 195 betragen wir können stattdessen let val t=... val 195=size t in print t endin den Körper der fnAbstraktion schreiben print(...). Das Entfernen eines Teils der Zeichenfolge führt zu einer Länge von weniger als 189, wodurch eine BindAusnahme ausgelöst wird.

Es gibt noch ein Problem: Der gesamte val 195=size tScheck könnte einfach fallengelassen werden. Wir können dies verhindern, indem wir das Häkchen so erweitern, dass es mit einem Tupel übereinstimmt : val t=... val(216,u)=(n+size t,t)in print u end, sodass das Entfernen des Häkchens zu einer ungebundenen Variablen führt u.

Insgesamt ergibt dies die folgende 195-Byte-Lösung:

val()=hd[(fn s=>let val t=s^"\""^String.toString s^"\")"val(195,u)=(size t,t)in print u end)"val()=hd[(fn s=>let val t=s^\"\\\"\"^String.toString s^\"\\\")\"val(195,u)=(size t,t)in print u end)"]

Die Anwendung des Golf - Tricks mit Operator Variablennamen wie !, $und %statt n, tund uum etwas Platz zu sparen (siehe diesen Tipp ) führt zur endgültigen 182 Byte - Version.

Alle anderen Teilzeichenfolgenentfernungen, die in der Erläuterung nicht explizit angegeben wurden, sollten zu einem Syntax- oder Typfehler führen.

Edit 1: length(explode t) ist gerade size t.
Edit 2: Vielen Dank an Anders Kaseorg für einen anderen Ansatz und den Hinweis auf eine "Sicherheitslücke".

Laikoni
quelle
−2 Bytes durch "\""direktes Schreiben und Verwenden String.toStringfür das Escaping.
Anders Kaseorg
Warten Sie, das ist schrecklich: MLton scheint das Programm zu akzeptieren "und erzeugt eine leere Ausgabe ( TIO ).
Anders Kaseorg
@AndersKaseorg Huh, das ist komisch. Es sollte jedoch möglich sein, dieses Problem mithilfe eines anderen zu beheben let ... in ... end.
Laikoni
@AndersKaseorg Ich habe das Problem behoben, hoffentlich ohne neue "Schwachstellen" einzuführen.
Laikoni
Eigentlich habe ich mir angesehen, "wie MLton als Programm akzeptiert wird , und es scheint, dass der Fehler in diesem Commit behoben wurde. Vielleicht sind Ihre 182 oder meine 180 in Ordnung, solange Sie die unveröffentlichte Git-Version von MLton angeben.
Anders Kaseorg