Welche allgemeinen Tipps haben Sie zum Golfen in Prolog? Ich bin auf der Suche nach Ideen, die allgemein auf Code-Golf-Probleme angewendet werden können, die zumindest etwas spezifisch für Prolog sind (z. B. sind Ein-Buchstaben-Variablen nicht spezifisch für Prolog, um die Größe von Programmen zu reduzieren).
Bitte geben Sie in Ihren Tipps an, ob es sich um eine spezifische Implementierung von Prolog handelt (z. B. SWI-Prolog-spezifische integrierte Funktionen).
Bitte posten Sie nur einen Tipp pro Antwort oder eine Liste von Tipps, die eng mit derselben Hauptidee zusammenhängen.
prolog
Tag ist irgendwie nutzlos. Solange wir keine Interpret Prolog-Herausforderung haben, brauchen wir sie nicht.Antworten:
Verwenden Sie Operatoren für Prädikatnamen
Es ist möglich, Prädikatenoperatoren als Namen zu vergeben, solange der Operator einer der vordefinierten Operatoren ( hier aufgelistet ) ist und nicht bereits als Prädikat definiert ist. Dies spart sowohl beim Definieren als auch beim Aufrufen des Prädikats einige Bytes, da Operator-Prädikate nicht in der normalen
name(arg1,arg2,etc..)
Form geschrieben werden müssen und aufgerufen werden können, wie es bei Operatoren zu erwarten ist.Für Prädikate mit einem oder zwei Argumenten können sie die Namen von unären bzw. binären Operatoren enthalten. Bei Prädikaten mit höherer Arität können Klammern bei der Mustererkennung immer noch vermieden werden. Wenn wir beispielsweise ein Prädikat haben
A+B+C:-...
, wandelt Prolog es anhand seiner Operatorprioritäts- und Assoziativitätsregeln in(A+B)+C:-...
ein Operatorprädikat um, mit dem das erste Argument einem Muster entsprichtA+B
. OderA-B+C*D:-...
das wird(A-B)+(C*D)
so, dass sein erstes Argument mit dem MusterA-B
und sein zweites mit dem Muster übereinstimmtC*D
.Beispiele
Die Ausgabe wird sein
X = 5.
Probieren Sie es online!
Logische Folge
Da DCGs syntaktischer Zucker für Prädikate sind, können ihnen auch Operatoren für Namen gegeben werden. Dies funktioniert wie erwartet, wenn Sie sie als DCGs entweder aus einer DCG aufrufen oder die
phrase
Prädikate oder andere Prädikate verwenden, die für die Arbeit mit DCGs entwickelt wurden. Wenn Sie sie als Prädikate aufrufen, sind Klammern erforderlich (z. B.A+B-->...
müssen sie wie+(A,B,...)
folgt aufgerufen werden ), da DCG-Prädikate zwei zusätzliche Argumente für ihre Differenzlisten verwenden. Bei DCGs mit Operatornamen und mehr als zwei Argumenten mit Operatormusterabgleich ist es wichtig, beim Aufrufen als Prädikat sicherzustellen, dass die mit dem Muster übereinstimmenden Operatoren korrekt verteilt sind.Die Angabe von Bedienernamen für DCGs, für die keine zusätzlichen Argumente erforderlich sind, kann nützlich sein, wenn Sie sie in Ihrem Programm aufrufen müssen, da Sie dies dann ohne Klammern tun können. Vorsicht ist geboten, da das, was Sie in Klammern speichern, durch zusätzlichen Abstand zum Parsen benachbarter Operatoren verloren gehen kann.
Beispiele
Ausgabe wird sein
Probieren Sie es online!
Vorbehalte
Mit den unären Operatoren
+
und-
interpretiert Prolog+20
oder-20
als Zahlen anstelle eines Aufrufs an ein+/1
oder ein-/1
Prädikat. Prädikate, die unär+
oder-
als Namen angegeben wurden, können weiterhin in Klammern (+(20)
,-(20)
) an der Nummer aufgerufen werden . Wenn zu vermeiden , ist das zusätzliche Bytes von Klammern wünschenswert , andere einstellige Operatoren wie\
,$
usw. kann stattdessen als Name verwendet werden.Die Kombination aus Pattern Matching und Operator-Prädikaten ist nicht ganz fehlerfrei. Wenn Sie zwei Prädikate haben, die denselben Operator wie ihr Name haben und bei denen eines streng allgemeiner ist als das andere, wird möglicherweise das allgemeinere zuerst aufgerufen, oder das weniger allgemeine schlägt fehl (abhängig von der Reihenfolge in der Quelle). . Wenn beispielsweise im obigen Beispiel
A-B+C*D
die Eingabe nicht übereinstimmt, ruft Prolog aufX+Y
. Dies führt zu einem Fehler, dalength/2
erforderlich istY
, dass es sich um eine Ganzzahl handelt, was nicht der Fall ist, da sie in der Form vorliegtC*D
. Dies kann einfach vermieden werden, indem sichergestellt wird, dass keine zwei Prädikate den gleichen Operator wie ihr Name haben, oder wenn dies fehlschlägt, werden Schnitte und eine sorgfältige Anordnung der Quelle verwendet.quelle
Versuchen Sie, jeden möglichen Fall in eine einzige Regel zu fassen
Die saubere Art, in Prolog zu programmieren, besteht darin, mehrere Regeln für dasselbe Prädikat zu deklarieren. Ein Prädikat zum Umkehren einer Liste mit einem Akkumulator sieht beispielsweise folgendermaßen aus:
In Code-Golf können wir die erste Regel entfernen und
;
am Ende der zweiten Regel eine hinzufügen, um das Rekursionsende zu codieren:Wir wissen, dass die erste Bedingung
r(T,[H|Z],R)
fehlschlägt, wenn T leer ist, dh wenn die Rekursion enden muss, und daher können wir unsere Kündigung als eine oder-Klausel danach hinzufügen.Das gleiche Prinzip funktioniert in vielen Situationen. Beachten Sie jedoch, dass es manchmal tatsächlich kürzer ist, eine andere Regel zu deklarieren, als dies zu tun.
quelle
Ein Trick, der häufig nützlich ist: Verwenden Sie CLP (FD) -Beschränkungen für Ganzzahlarithmetik, um Prädikate zu erhalten, die automatisch in mehrere Richtungen verwendet werden können, wodurch Bedingungen und dedizierte Verzweigungen und Varianten vermieden werden.
Verwenden Sie B-Prolog oder GNU Prolog, wenn solche Einschränkungen sofort verfügbar sind, ohne dass Bibliotheken geladen werden müssen.
quelle
library(clpfd)
es eine vorinstallierte oder zumindest automatisch geladene Bibliothek auch in SWI-Prolog gibt. Es kann einige Jahre dauern, bis die deklarative Arithmetik von allen Benutzern, die inzwischen jahrzehntelange Erfahrung mit veralteten Funktionen auf niedriger Ebene gesammelt haben, vollständig verstanden und geschätzt wird. Je häufiger Sie CLP (FD) verwenden und befürworten, desto eher erhalten wir es standardmäßig. Bis dahin können Sie einfach ausgedrückt:- use_module(library(clpfd)).
in Ihrem~/.swiplrc
und gerechten Staat , dass Sie die „Variante“ von SWI-Prolog verwenden.Verwenden Sie arithmetische Operatoren als Tupelkonstruktoren und Konsolenpaare
Wenn Sie eine einzelne Struktur übergeben müssen, die aus zwei oder mehr Werten besteht, sollten Sie am besten eine Liste verwenden, z
[A,B]
. Das ist allerdings sehr ausführlich.Es gibt eine Alternative. Prolog-Werte können eine ziemlich willkürliche verschachtelte Struktur speichern, die nicht ausgewertet wird. Hier ist ein Beispiel, wie das funktioniert:
member(A,B)
ist in dieser Situation nur ein benanntes Tupel, und das Äußeremember
(das ein Funktionsaufruf ist) behandelt es als solches.Auch wenn benannte Tupel in der Programmierung mit Prolog ohne Golfspiel ziemlich nützlich sind, scheinen sie möglicherweise noch ausführlicher zu sein als der Listenansatz. Wir können jedoch im Namen des Tupelkonstruktors ziemlich beliebige Zeichen verwenden (vorausgesetzt, sie werden korrekt in Anführungszeichen gesetzt). Anstelle von so etwas Süßem
member
oder einem einzelnen Charaktera
können wir so etwas machen:Hier sind
'-'
und unsere Tupelkonstrukteure'/'
. Und es ist interessant zu sehen, was der hübsche Drucker mit ihnen gemacht hat. Für die Tupel wird die Infixnotation verwendet. Dies ist wirklich knapp und analysiert auf die gleiche Weise wie die vergleichbare arithmetische Operation. (Dies erklärt auch, warum Arithmetikis
nicht verwendet wird=
;A = 1+2
würde sichA
mit dem Tupel vereinigen'+'(1,2)
, so dass eine separate Syntax erforderlich ist, um den nicht bewerteten arithmetischen Ausdruck tatsächlich auszuwerten.) Da ein Tupelkonstruktor etwas aufgerufen werden muss , können Sie auch ein Zeichen verwenden, das eine Knappheit aufweist Syntax (und als Bonus-
und/
Dies sind auch einige der am häufigsten verwendeten Optionen in nicht-golfenem Code, wenn ein schneller Tupelkonstruktor anstelle eines aussagekräftigen Konstruktors verwendet werden soll, ähnlich wie diesi
häufig als Schleifenvariable verwendet wird Eingabe und Ausgabe, wenn Sie aus irgendeinem Grund ein Tupel dort haben wollen).'-'
und'/'
sind eine gute Wahl für Tupelkonstruktoren, da sie einen guten und nützlichen Vorrang haben und es Ihnen ermöglichen, Tupel-Literale kurz zu schreiben. Beachten Sie jedoch, dass Sie sich keine Gedanken über die Priorität machen müssen, wenn im Programm Zwischenwerte erstellt werden. Prolog speichert die Tupel nicht als Quellcode, sondern als Baum. Hübsche Drucker können sie eindeutig ausgeben:Da die Tupelsyntax so kurz
f(A,B)
ist ( nicht kürzer alsf(A-B)
), können Sie mehrere Prädikatsargumente kostenlos durch Tupel ersetzen. Wenn also ein Prädikat zwei oder mehr seiner Argumente an ein anderes Prädikat übergeben muss, können Sie sie häufig in Form bringen ein Tupel und übergeben Sie einfach das Tupel (obwohl dies erfordert, dass alle Aufrufe des Prädikats zusätzlich zum Prädikat selbst geändert werden, um eine geeignete Mischung aus Tupelkonstruktoren und Kommas zu verwenden).Ein weiterer Vorteil dieser Syntax besteht darin, dass Sie Listen intern verwenden müssen (anstatt mit Standardprädikaten zusammenzuarbeiten). Eine Liste besteht im Grunde genommen nur aus verschachtelten Konsolenzellen, und eine Konsolenzelle ist nur ein Tupel mit Konstruktor
'.'
, wie hier zu sehen ist:Wenn Ihr Code Listen "manuell" verwendet, kann es sehr sinnvoll sein, einen weniger umfangreichen Tupelkonstruktor als zu verwenden
'.'
. Eine häufige Wahl für mich ist die Darstellung einer Nachteile-Zelle als'/'(Tail,Head)
(da es sich um die am besten lesbare handelt, die Sie in der Debug-Ausgabe erhalten können, ohne Zeichen zu verschwenden). Beachten Sie, dass Sie wahrscheinlich auch Ihr eigenes[]
Äquivalent haben möchten . Sie könnten es verwenden,[]
aber es ist zwei Bytes lang und es gibt viele Ein-Byte-Atome (alle Kleinbuchstaben), die Sie stattdessen verwenden können.So zum Beispiel die folgende Liste:
könnte in eine manuelle Darstellung mit der gleichen Anzahl von Zeichen wie folgt umgewandelt werden:
[H|T]
Dies bringt den Vorteil mit sich, dass Übereinstimmungen im Stil von Mustern jetzt engerT/H
und ein Test mit der leeren Listex
eher als mit der längeren Liste geschrieben werden können[]
. (Natürlich kommt dies mit dem offensichtlichen Nachteil , dassmember
,append
usw., wird nicht auf dieser Darstellung arbeiten.)quelle
-
und sind keine einfachen Anführungszeichen erforderlich/
. Das sind schon ganz normale Atome..
, vorausgesetzt, die folgenden Zeichen sind weder%
ein Layout noch ein Anführungszeichen .Kürzere Syntax für Listen mit Listen und eine Möglichkeit zum Deklarieren von Karten
Sie können Bytes in Listen von Listen speichern. Wenn Sie eine Liste haben
[[1,2],[3,4]]
, können Sie sie tatsächlich als deklarieren[1:2,3:4]
, wodurch 4 Klammern = 4 Bytes gespeichert werden. Beachten Sie, dass Sie etwas anderes als:
(zum Beispiel^
) verwenden können.1:2
ist in diesem Fall eigentlich keine Liste (während[1,2]
war), sondern wird intern als dargestellt:(1,2)
. Daher können Sie keine Prädikate verwenden, die auf Listen in Unterlisten mit Doppelpunkten funktionieren.Dieser Trick wird hauptsächlich zum Deklarieren von Maps verwendet, dh einer Liste von Schlüsseln, an die Werte angehängt sind. Wenn Sie beispielsweise eine Karte deklarieren möchten
M
, die die Schreibweise einer Ziffer in Englisch und Französisch enthält, können Sie Folgendes tun:Sie können dann beispielsweise Elemente der Karte mit einem eingebauten Prädikat wie z. B. abrufen
member/2
. Zum Beispiel, wenn Sie die Ziffer und englisches Wort entsprechend wollen'Quatre'
inM
, könnten Sie tun:quelle
[[1,2],[3,4]]
as1*2+3*4
,+(*(1,2),*(3,4))
also auch nur ein Byte verwenden, wo Sie sonst zwei benötigen würden, um Klammern zu öffnen und zu schließen."abc"
Statt[a,b,c]
Ein guter Trick: Wenn Sie einen Fehler machen müssen , verwenden Sie etwas, das false / 0 entspricht , aber kürzer ist, wie zum Beispiel:
quelle
\+!
eine 3-Byte-seltsame Methode zum Fehlschlagen verwenden, die den Schnitt nicht tatsächlich auslöst!
(siehe dies für den Grund ). Ich glaube nicht, dass es möglich ist, in weniger als 3 Bytes zu versagen.\+!
Kleber links für andere Grafikzeichen,0=1
Kleber links für Namen.Verwenden Sie ein Prädikat mit verschiedenen Aufrufmodi erneut
Sie können beispielsweise eine Struktur mit demselben Prädikat analysieren und drucken, einmal mit einem variablen Argument und ein anderes Mal mit einem Grundbegriff. Ich habe diesen Ansatz in Make the Stretchy Snakes Kiss verwendet . Dies ist natürlich nicht bei allen Herausforderungen möglich.
quelle