Suche…


Einführung

Viele moderne Prolog-Systeme werden ständig weiterentwickelt und verfügen über neue Funktionen, um die klassischen Mängel der Sprache zu beheben. Leider stellen viele Prolog-Lehrbücher und sogar Lehrkurse nur den veralteten Prolog vor. Dieses Thema soll veranschaulichen, wie der moderne Prolog einige der Probleme und die etwas verkrampfte Syntax überwunden hat, die im älteren Prolog vorkommen und möglicherweise noch eingeführt werden.

CLP (FD) für Ganzzahlarithmetik

Traditionell führte Prolog die Arithmetik mit den Operatoren is und =:= . Mehrere aktuelle Prologs bieten jedoch CLP (FD) (Constraint Logic Programming über finite Domains) als sauberere Alternative für die Ganzzahlarithmetik an. CLP (FD) basiert darauf, die Einschränkungen zu speichern, die für einen ganzzahligen Wert gelten, und diese im Speicher zusammenzufassen.

CLP (FD) ist eine Erweiterung in den meisten Prologs, die dies unterstützen, und muss daher explizit geladen werden. Nach dem Laden kann die #= -Syntax die Stelle von is und =:= einnehmen. Zum Beispiel in SWI-Prolog:

?- X is 2+2.
X = 4.

?- use_module(library(clpfd)).
?- X #= 2+2.
X = 4.

Im Gegensatz zu is , kann #= einfache Gleichungen lösen und in beide Richtungen vereinheitlichen:

?- 4 is 2+X.
ERROR: is/2: Arguments are not sufficiently instantiated

?- 4 #= 2+X.
X = 2.

CLP (FD) stellt eine eigene Generatorsyntax bereit.

?- between(1,100,X).
X = 1;
X = 2;
X = 3...

?- X in 1..100.
X in 1..100.

Beachten Sie, dass der Generator nicht wirklich läuft: Es wird nur die Bereichsbeschränkung gespeichert, sodass spätere Einschränkungen damit kombiniert werden können. Der Generator kann mit dem label Prädikat ausgeführt werden (und brutale Zwangsbedingungen):

?- X in 1..100, label([X]).
X = 1;
X = 2;
X = 3..

Die Verwendung von CLP kann eine intelligente Reduzierung von Brute-Force-Fällen ermöglichen. Verwenden Sie zum Beispiel die Integer-Arithmetik im alten Stil:

?- trace.
?- between(1,10,X), Y is X+5, Y>10.
...
Exit: (8) 6 is 1+5 ? creep
Call: (8) 6 > 10 ? creep
...
X = 6, Y = 11; ...

Prolog durchläuft immer noch die Werte 1 - 5, auch wenn aus den gegebenen Bedingungen mathematisch nachweisbar ist, dass diese Werte nicht nützlich sind. Verwendung von CLP (FD):

?- X in 1..10, Y #= X+5, Y #> 10.
X is 6..10,
X+5 #= Y,
Y is 11..15.

CLP (FD) führt die Berechnungen sofort aus und ermittelt die verfügbaren Bereiche. Durch das Hinzufügen der label([Y]) durchläuft X nur die nützlichen Werte. 6..10. In diesem Spielzeugbeispiel erhöht dies die Leistung nicht, da bei einem so kleinen Bereich wie 1 bis 10 die Algebra-Verarbeitung so lange dauert, wie es die Schleife getan hätte. Wenn jedoch ein größerer Zahlenbereich verarbeitet wird, kann dies die Rechenzeit erheblich reduzieren.

Die Unterstützung für CLP (FD) ist zwischen Prologs variabel. Die anerkannte beste Entwicklung von CLP (FD) findet sich in SICStus Prolog, das kommerziell und teuer ist. SWI-Prolog und andere offene Prologs werden häufig implementiert. Visual Prolog enthält CLP (FD) nicht in seiner Standardbibliothek, obwohl Erweiterungsbibliotheken dafür verfügbar sind.

Forall statt ausfallbedingter Schleifen

Einige "klassische" Prolog-Lehrbücher verwenden immer noch die verwirrende und fehleranfällige fail Schleifensyntax, bei der ein fail Konstrukt verwendet wird, um das Zurückverfolgen zu erzwingen, um ein Ziel auf jeden Wert eines Generators anzuwenden. So drucken Sie beispielsweise alle Zahlen bis zu einem bestimmten Grenzwert:

fdl(X) :- between(1,X,Y), print(Y), fail.
fdl(_).

Die große Mehrheit der modernen Prologe benötigt diese Syntax nicht mehr, sondern stellt stattdessen ein Prädikat höherer Ordnung bereit.

nicer(X) :- forall(between(1,X,Y), print(Y)).

Dies ist nicht nur viel einfacher zu lesen, sondern wenn ein fehlerhaftes Ziel anstelle des Druckvorgangs verwendet wird , wird der Fehler korrekt erkannt und weitergegeben - während Zielfehler in einer ausfallsgesteuerten Schleife mit dem erzwungenen Fehler verwechselt werden das treibt die Schleife.

Visual Prolog bietet einen benutzerdefinierten syntaktischen Zucker für diese Schleifen in Kombination mit Funktionsprädikaten (siehe unten):

vploop(X) :- foreach Y = std::fromTo(1,X) do
                 console::write(X)
             end foreach.

Obwohl dies für die Schleife als zwingend notwendig erscheint, folgt sie dennoch den Prolog-Regeln: Insbesondere ist jede Iteration des foreach- Bereichs ein eigener Bereich.

Prädikate im Funktionsstil

Traditionell wurden in Prolog "Funktionen" (mit einer Ausgabe und gebundenen Eingaben) als reguläre Prädikate geschrieben:

mangle(X,Y) :- Y is (X*5)+2.

Dies kann die Schwierigkeit schaffen, dass, wenn ein Prädikat mit Funktionsstil mehrmals aufgerufen wird, temporäre Variablen "verkettet" werden müssen.

multimangle(X,Y) :- mangle(X,A), mangle(A,B), mangle(B,Y).

In den meisten Prologs kann dies vermieden werden, indem ein alternativer Infix-Operator anstelle von is der die Ausdrücke einschließlich der alternativen Funktion erweitert.

% Define the new infix operator
:- op(900, xfy, <-).

% Define our function in terms of the infix operator - note the cut to avoid
% the choice falling through
R <- mangle(X) :- R is (X*5)+2, !.

% To make the new operator compatible with is..
R <- X :-
    compound(X),            % If the input is a compound/function
    X =.. [OP, X2, X3],     % Deconstruct it
    R2 <- X2,               % Recurse to evaluate the arguments
    R3 <- X3,
    Expr =.. [OP, R2, R3],  % Rebuild a compound with the evaluated arguments
    R is Expr,              % And send it to is
    !.
R <- X :- R is X, !.        % If it's not a compound, just use is directly

Wir können jetzt schreiben:

multimangle(X,Y) :- X <- mangle(mangle(mangle(Y))).

Einige moderne Prologs gehen jedoch noch weiter und bieten eine benutzerdefinierte Syntax für diese Art von Prädikat. Zum Beispiel in Visual Prolog:

mangle(X) = Y :- Y = ((X*5)+2).
multimangle(X,Y) :- Y = mangle(mangle(mangle(X))).

Beachten Sie, dass sich der Operator <- und das Prädikat für den funktionalen Stil immer noch als Beziehungen verhalten. Es ist für sie zulässig, Auswahlpunkte zu haben und mehrfache Vereinheitlichung durchzuführen. Im ersten Beispiel verhindern wir dies mit Schnitten. In Visual Prolog ist es üblich, die funktionale Syntax zu verwenden, um Beziehungen und X = (std::fromTo(1,10))*10 normale Weise zu X = (std::fromTo(1,10))*10 Beispielsweise ist das Ziel X = (std::fromTo(1,10))*10 mit Bindungen X = 10 erfolgreich X = 20, X = 30, X = 40 usw.

Fluss- / Modusdeklarationen

Bei der Programmierung in Prolog ist es nicht immer möglich oder wünschenswert, Prädikate zu erstellen, die sich für jede mögliche Kombination von Parametern vereinheitlichen. Zum Beispiel ist das Prädikat between(X,Y,Z) das ausdrückt, dass Z numerisch zwischen X und Y ist. Es ist in den Fällen, in denen X, Y und Z alle gebunden sind, leicht zu implementieren (entweder Z liegt zwischen X und Y oder es ist nicht) oder wo X und Y gebunden sind und Z frei ist (Z verbindet alle Zahlen zwischen X und Y oder das Prädikat versagt, wenn Y <X); In anderen Fällen, beispielsweise wenn X und Z gebunden sind und Y frei ist, gibt es möglicherweise unendlich viele Vereinigungen. Obwohl dies implementiert werden kann, ist dies normalerweise nicht der Fall.

Flussdeklarations- oder Modusdeklarationen ermöglichen eine explizite Beschreibung des Verhaltens von Prädikaten, wenn sie mit unterschiedlichen Kombinationen gebundener Parameter aufgerufen werden. Im Falle von between würde die Erklärung lauten:

%! between(+X,+Y,+Z) is semidet.
%! between(+X,+Y,-Z) is nondet. 

Jede Zeile gibt ein potenzielles Aufrufmuster für das Prädikat an. Jedes Argument ist mit + um auf Fälle hinzuweisen, in denen es gebunden ist, oder - , um Fälle anzuzeigen, in denen dies nicht der Fall ist (es gibt auch andere Dekorationen für komplexere Typen wie Tupel oder Listen, die möglicherweise teilweise gebunden sind). Das Schlüsselwort after ist das Verhalten des Prädikats in diesem Fall und kann eines davon sein:

  • det wenn das Prädikat immer ohne Auswahlpunkt erfolgreich ist. Zum Beispiel add(+X,+Y,-Z) ist det weil das Hinzufügen von zwei gegebenen Zahlen X und Y immer genau eine Antwort hat.
  • semidet wenn das Prädikat erfolgreich ist oder fehlschlägt, ohne semidet . Wie oben ist between(+X,+Y,+Z) semidet weil Z entweder zwischen X und Y liegt oder nicht.
  • multi wenn das Prädikat immer erfolgreich ist, aber Wahlpunkte haben kann (aber auch nicht). Zum Beispiel wäre factor(+X,-Y) multi da eine Zahl immer mindestens einen Faktor hat - sich selbst -, aber mehr.
  • nondet wenn das Prädikat mit nondet erfolgreich sein kann oder fehlschlägt. between(+X,+Y,-Z) ist zum nondet da es mehrere mögliche Vereinigungen von Z zu Zahlen zwischen X und Y geben kann, oder wenn Y <X, dann keine Zahlen dazwischen und das Prädikat schlägt fehl.

Fluss- / Modusdeklarationen können auch mit der Argumentbeschriftung kombiniert werden, um zu klären, was Begriffe bedeuten, oder mit der Typisierung. Zum Beispiel between(+From:Int, +To:Int, +Mid:Int) is semidet .

In reinen Prologs sind Fluss- und Modusdeklarationen optional und werden nur für die Dokumentationserstellung verwendet. Sie können jedoch äußerst hilfreich sein, um Programmierern dabei zu helfen, die Ursache von Instanziierungsfehlern zu ermitteln.

In Mercury sind Fluss- und Modusdeklarationen (und -typen) obligatorisch und werden vom Compiler validiert. Die verwendete Syntax ist wie oben.

In Visual Prolog sind auch Fluss- und Modusdeklarationen und -typen obligatorisch und die Syntax ist unterschiedlich. Die obige Erklärung würde wie folgt geschrieben:

between : (int From, int To, int Mid) determ (i,i,i) nondeterm (i,i,o).

Die Bedeutung ist dieselbe wie oben, jedoch mit den Unterschieden, dass:

  • Die Fluss- / Modusdeklarationen werden von den Typdeklarationen getrennt (da davon ausgegangen wird, dass der Fluss- / Modus für ein einzelnes Prädikat nicht mit der Typüberladung variiert).
  • i und o werden für + und - und mit den auf Reihenfolge basierenden Parametern abgeglichen;
  • Die verwendeten Begriffe sind unterschiedlich. det wird procedure , semidet wird determ und nondet wird nondeterm ( multi ist noch multi ).


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow