TU Wien:Fortgeschrittene logische Programmierung VU (Neumerkel)/Prüfungsordner 2010

Aus VoWi
Zur Navigation springen Zur Suche springen

Prüfungskatalog aus der GUPU-Übungsumgebung[Bearbeiten | Quelltext bearbeiten]

Die hier angeführten Fragen wurden aus der GUPU-Übungsumgebung entnommen, und dienen zur Vorbereitung auf die Prüfung.

setof/3 allg. erklären[Bearbeiten | Quelltext bearbeiten]

Das Prädikat setof/3 wird verwendet um Lösungsmengen kompakt zu beschreiben. Allgemeiner Syntax: setof(Lösungsschema,Ziel,Lösungsmenge). Wobei das Lösungsschema alle Variablen enthält, die in der Lösungsmenge angeführt werden sollen und die Lösungsmenge eine sortierte redundanzfreie Liste ist.

Beispiel:

:- setof(Kind,kind_von(Kind,Elternteil),Kinder). % Liefert alle Eltern und deren Kinder (als Liste).

Wichtigste Eigenschaft von setof/3 ist, dass es nicht monoton ist. Monotonie bedeutet, dass wenn ich z.B. ein neues Faktum hinzufüge, dass die Lösungsmenge entweder gleich bleibt, oder wächst. Durch das setof/3 wird allerdings diese Eigenschaft verloren, da in der Lösung von setof/3 sich immer nur maximal eine Lösung findet. Was sich ändert ist der Inhalt der Liste.

Werden also Aussagen höherer Ordnung über die Lösungsmenge getroffen, kann es sein, dass diese Gültigkeit verlieren wenn neue Fakten hinzukommen. Beispiel:

:- Kinder=[_],  setof(Kind, kind_von(Kind, Elternteil), Kinder). % Liefert alle Eltern die genau ein Kind haben

Fügt man jetzt ein Fakt hinzu, das aussagt, dass eines der Elternteile ein der Lösungsmenge ein weiteres Kind hat, schrumpft plötzlich die Lösungsmenge der oben gestellten Anfrage.

^ in setof/3[Bearbeiten | Quelltext bearbeiten]

Das ^ kommt aus den Lambda-Expressions und kann verwendet werden um Variablen auszublenden.

Beispiel:

Liefert alle Kinder in einer einzigen Liste. Elternteile sind uninteressant und sollen nicht beachtet werden. Also wird Elternteil global ausgeblendet:

:- setof(Kind, Elternteil^kind_von( Kind, Elternteil), Kinder).

Was sind Aggregationen?[Bearbeiten | Quelltext bearbeiten]

Eine Aggregationsfunktion ist eine Funktion die eine Menge übernimmt, und einen einzigen Wert zurückliefert (z.B. Summe, Durchschnitt, Anzahl der Elemente, Erstes Element,...)

Beispiel:

:- setof(Kind,Elternteil^kind_von(Kind,Elternteil),Kinder), list_length(Kinder,L). % Liefert in der Variable L die Anzahl aller Kinder.

nonvar/1, var/1, ground/1, ==/2[Bearbeiten | Quelltext bearbeiten]

Diese vordefinierten Prädikate werden verwendet, um zu testen, ob der Ausdruck zum Zeitpunkt des Beweises Variablen enthält oder nicht. Dadurch sind diese Prädikate deklarativ nicht mehr erklärbar.

ground/1 überprüft ob der Term komplett variablenfrei ist oder nicht. nonvar/1 testet, ob auf oberster Ebene keine freie Variable mehr vorkommt, var/1 testet, ob eine Variable vorkommt und ==/2 testet, ob die beiden Terme zum Zeitpunkt des Beweises nicht mehr verschieden sind.

Einige der folgenden Paare sind deklarativ gleichbedeutend, jeweils eine Variante scheitert jedodch.

:- T=term,nonvar(T).   % Argument von nonvar ist zum Zeitpunkt des Beweises bereits gebunden
:/- nonvar(T), T=term. % Argument von nonvar ist zum Zeitpunkt des Beweises NICHT gebunden

:- T=f(X),nonvar(T). % Term enthält keine Variablen auf oberster Ebene

:- var(T). % Term enthält ungebundene Variablen

:/- T=term,var(T).   % Argument von var enthält zum Zeitpunkt des Beweises KEINE freien Variablen
:- var(T), T=term.   % Argument von var enthält zum Zeitpunkt des Beweises freie Variablen

:/- T=f(Variable),ground(T). % Term enthält noch ungebundene Variablen und ist daher noch kein Grundterm.
:- T=f(x),ground(T). % Term ist ein Grundterm.

:- f(X) = f(Y),f(X) == f(Y).  % Terme zuerst bewiesen, dann auf Gleicheit überprüft
:/- f(X) == f(Y),f(X) = f(Y). % Terme zuerst auf Gleicheit überprüft, dann bewiesen.

metacall erklären[Bearbeiten | Quelltext bearbeiten]

Wie auch in anderen Programmiersprachen kann in Prolog ein Ziel eine Variable sein, die allerdings zum Zeitpunkt des Aufruft gebunden sein muss. Hierzu wird das Prädikat call/N verwendet.

Beispiel:

:- Ziel=true, call(Ziel). % Ruft das Ziel true auf.
:- call(Ziel), Ziel=true. % Fehler, da Variable ungebunden.

Wie ist der metacall in Prolog kodierbar?[Bearbeiten | Quelltext bearbeiten]

Das entsprechend Call-Ziel (für jedes Ziel, das im Programm steht muss eines generiert werden) wird zur Laufzeit angelegt.

Beispiel:

call(Ziel) :-
   var(Ziel),
   fehler("Fehler, da Ziel ungebundene Variablen enthält").
call(true).
call(nat_nat_summe(A,B,C))  :-
   nat_nat_summe(A,B,C).
call(kind_von(Kind,Person)) :-
   kind_von(Kind,Person).
% usw.

mapcar/3[Bearbeiten | Quelltext bearbeiten]

Entspricht dem mapcar aus Lisp und ähnelt maplist/3.

Mit den in GUPU verfügbaren Varianten von maplist (z.B. maplist/3) kann die Funktionalität von mapcar/3 erreicht werden. maplist/3 operiert auf Tupeln, wobei Element 1 aus Liste 1 und Element 2 aus Liste 2 kommt:

translated(a, b).
translated(c, b).

:- maplist(A^B^translated(A, B), [a,c,a,a,a,a,c,a]) % A = a, B = b, C = [b,b,b,b,b,b,b,b]

apply/2[Bearbeiten | Quelltext bearbeiten]

Es nimmt das zweite Argument und verwendet die einzelnen Elemente als eigenständige Argumente für das Prädikat, welches als erstes Argument übergeben wird. apply ist allerdings in GUPU nicht verfügbar.

:- apply( is, [X, A+B]). % X is A+B.
:- apply( apply, [apply, [kind_von, [Kind, Person]]]). % kind_von( Kind, Person).

vgl. maplist/3 mapcar/3[Bearbeiten | Quelltext bearbeiten]

Bisher war ich der Meinung, dass maplist und mapcar sich darin unterscheiden, dass maplist/3 call/n verwendet und mapcar/3 apply/2. Allerdings klingt bei SWI-Prolog es so, dass ursprünglich apply/2 verwendet wurde, allerdings aktuell wird call/2 verwendet.

Zumindest ist apply/2 langsamer als call/n

Womöglich ist der größte Unterschied, dass es sich bei mapcar/3 um explizite, bei maplist/3 allerdings um implizite Instanzierung (Verwendung von call/2 vs copy_term/2) handelt?

Achtung: In Sixstus-Prolog (in der GUPU verwendet) gibt es das Prädikat apply/2 nicht, sondern nur call/N.

Meta-Interpreter allg.[Bearbeiten | Quelltext bearbeiten]

Ein Meta-Interpreter (auch self-interpreter) ist ein Interpreter der in der Sprache geschrieben wurde, die er interpretiert. Meta-zirkulär ist nicht exakt definiert. Einige Definitionen:

  • Ein Interpreter der in der Lage ist sich selbst zu interpretieren.
  • Ein Interpreter der Funktionen der interpretierten Sprache umsetzt, indem er diesselben Funktionen in seiner Implementierungssprache verwendet (absorption)

Die Voraussetzungen um einen Meta-Interpreter schreiben zu können ist, dass man eine Datenstruktur zur Darstellung eines Programmes definiert. Idealerweise ist die Definition = Sprachdefinition. Und dass die Anzahl der Sprachmittel und Komplexität der Syntax verhältnismäßig ausgeglichen ist zur Mächtigkeit der Sprachmittel.

  • Maschinensprache: Einfache Syntax, einfache Semantik
  • Prozedurale Sprachen: Komplexe Syntax, meist kein (eindeutiger) Abstrakter Syntaxbaum, viele Sprachelemente
  • Deklarative Sprachen: Abstrakter Syntaxbaum meist als Term, wenige Sprachelemente.

Meta-Interpreter werden gerne verwendet um neue Erweiterungen für Prolog zu prototypen.

Wodurch wird Komplexität des Meta-Interpreters bestimmt?[Bearbeiten | Quelltext bearbeiten]

Die Komplexität eines Meta-Interpreters wird durch den gewünschten Detaillierungsgrad bestimmt. Wichtig sind hier einfache Datenstrukturen, reification (also das explizite Ausprogrammieren von Teilen der Sprache: Unifikation, Bindungsumgebung für Variablen, Prozeduraufruf und backtracking) und absorption (also die implizite Wiederverwendung von Sprachmitteln).

Rolle des AST (abstrakter Syntaxbaum) in meta-interpretern[Bearbeiten | Quelltext bearbeiten]

Ein Abstrakter Syntaxbaum ist eine baumartige Datenstruktur die in Programmiersprachen meist während des Parsens erstellt wird und als interne Repräsentation des Programmes verwendet wird, um effizient zu suchen und damit zu arbeiten.(Bsp. DOM-Baum in HTML).

?? Ein AST ist die Definition der Sprache. Nicht immer ist die Definition der Sprache ein AST. ??

An Hand des ASTs kann die Sprache interpretiert werden, da der AST abstrakt der Sprache entspricht. Ohne AST wird dies allgemein nicht unbedingt möglich sein. Beispielsweise das umbenennen von Prädikaten hat zur Folge, dass alle Prädikate in den Zielen ebenfalls umbenannt werden müssen. In einem AST entspricht dies einem Suchen-und-Umbennenen. Gibt es keinen AST, kann man höchstens ein Suchen-und-Ersetzen anwenden, aber man kann nicht sicher sein, ob man alle "Entsprechungen" findet, bzw. ob man nicht zuviel umbenennt.

if/3 vs. ( -> ; )[Bearbeiten | Quelltext bearbeiten]

(-> ;) verwendet ein cut (!), wiederum if/3 wertet die Bedingung in beiden Regeln aus, allerdings in der "Else-Regel" \+ Bedingung.

Beispiel:

:- if(a==a,X#=3,X#=4). % X=3.
:- if(a==B,X#=3,X#=4). % X=4.

Der Dadurch entstehende Unterschied ist, dass im Falle des Cut die verschiedenen Lösungen der Bedingung nicht aufgezählt werden:

if_keincut(Bedingung, Then, _Else) :-
  Bedingung,
  Then.
if_keincut(Bedingung, _Then, Else) :-
  \+ Bedingung,
  Else.

if_cut(Bedingung, Then, _Else) :-
  Bedingung,
  !,
  Then.
if_cut(_Bedingung, _Then, Else) :-
  Else.

bedingung(wert1).
bedingung(wert2).

:- if_keincut(bedingung(A), X=1, X=2)
% A = wert1, X=1.
% A = wert2, X=1.

:- if_cut(bedingung(A), X=1, X=2)
% A = wert1, X=1.

Blockdeklarationen[Bearbeiten | Quelltext bearbeiten]

Blockdeklarationen dienen zur Verbesserung der Berechnungsstrategie und sind eine Anwendung von coroutining. Mit ihrer Hilfe wird die Ausführung so lange verzögert, bis durch '-' gekennzeichnete Argumente gebunden sind.

Beispiel für Blockdekls.[Bearbeiten | Quelltext bearbeiten]

:- block freeze(-,?).
freeze(_Var,Ziel) :-
  Ziel.

:- block is_list(-).
is_list([]).
is_list([_X|Xs]) :-
  is_list(Xs).

:- block greater_than(-,?), greater_than(?,-).
greater_than(A,B) :-
  A > B.
:- block smallerequal_than(-,?), smallerequal_than(?,-).
smallerequal_than(A,B) :-
  A =< B.

:- greater_than(A,B).
@@ % user:greater_than(A,B).
@@ %% Eine Antwort gefunden.

:- greater_than(4,3).
@@ % true.
@@ %% Eine Antwort gefunden.

when/2[Bearbeiten | Quelltext bearbeiten]

:- when( Wenn, Dann). % Unsinn

Wenn Wenn gilt, wird Dann ausgewertet.

Mögliche Bedingungen: nonvar/1, ground/1, ?=/2, ','/2, ';'/2

:- when( nonvar(A), X=A), when( nonvar(B), X=B). 

Dieser Ausdruck verwendet ein Block, und es wird quasi gewartet, bis der erste Ausdruck z.B. eine Grundterm ist. Entsprechend machen auch andere Ausdrücke (wie var/1) keinen Sinn.

Suchverfahren[Bearbeiten | Quelltext bearbeiten]

Darstellung von Iterationen[Bearbeiten | Quelltext bearbeiten]

make_p(N,N).

done_p(1).

next_p(N0,N) :-
  N0 > 1,
  0 is N0 mod 2,
  N is N0 // 2.
next_p(N0,N) :-
  N0 > 1,
  1 is N0 mod 2,
  N is 3 * N0 + 1.

iteration_I(N) :-
  make_p(N,S0).

iter_I(S) :-
  done_p(S).
iter_I(S0) :-
  next_p(S0,S), % \+ done_p(S0)
  iter_I(S).

% interessantere Variante mit Darstellung von Zwischenergebnissen: 

iteration_II(N, IS) :-
  make_p(N, S0),
  iter_II(S0, S),
  show(S, IS).

show(S, ende(S)) :-
  done_p(S).
show(S, zw(S)) :-
  \+ done_p(S).

iter_II(S, S).
iter_II(S0, S) :-
  next_p(S0, S1),
  iter_II(S1, S).

:- iteration_II(27, IS).
@@ % IS = zw(27).
@@ % IS = zw(82).
@@ % IS = zw(41).
@@ % IS = zw(124).
.
.
@@ %% IS = ende(1).

Die Variante mit der Darstellung von Zwischenergebnissen bedient sich eines Tricks. Durch den Fakt iter_II(S, S) ist das Ziel iter_II(S0, S) in iteration_II(N, IS) für jede von next_p(S0, S1) erzeugte Zahl einmal erfüllt und es wird daraufhin das nächste Ziel show(S, IS) bewiesen und IS als (Zwischen-)Lösung geliefert. Fordert der Benutzer jetzt eine weitere Lösung an, wird im Suchbaum ein anderer Zweig gewählt. Zuerst wird versucht eine alternative Regel für show(S, IS) zu finden. So eine Alternative gibt es nicht, da die vorhin ausgegebene Zahl das Ziel done_p(S) nicht erfüllt. Also wird eine alternative Regel für das Ziel iter_II(S1, S) innerhalb von iter_II(S0, S) gewählt, die diesmals nicht iter_II(S, S) ist. Also wird die Regel iter_II(S0, S) verwendet, next_p(S0, S1) (next_p(VorigeZahl, NächsteZahl)) erzeugt die nächste Zahl und das Spiel geht von vorne los.

Dass der Fakt (und seine Position) äußerst bedeutsam sind, zeigen die folgenden Beispiele:

Vereinfachen des Fakt[Bearbeiten | Quelltext bearbeiten]

iter_II(1, 1).
iter_II(S0, S) :-
  next_p(S0, S1),
  iter_II(S1, S).

:- iteration_II(27, IS).
@@ %% IS = ende(1).

Hier kann der Fakt durch die Unifikation erst ganz am Schluss als alternative Regel das Ziel iter_II(S1, S) gewählt werden. Bis dahin wird stets weiter die rekursive Regel gewählt, wodurch show(S, IS) erst ganz am Schluss bewiesen wird. Formal wird dadurch auch eine andere Lösungsmenge beschrieben, da formal natürlich auch die zw(X) zu der Lösungsmenge gehören. Informell können aber nach wie vor die selben Zahlen geprüft werden.

Verschieben des Fakt[Bearbeiten | Quelltext bearbeiten]

iter_II(S0, S) :-
  next_p(S0, S1),
  iter_II(S1, S).
iter_II(S, S).

:- iteration_II(27, IS).
@@ %% IS = ende(1).
@@ %% IS = ende(2).
@@ %% IS = ende(4).
@@ %% IS = ende(8).
.
.
@@ % IS = zw(27).
@@ % IS = zw(82).
@@ % IS = zw(41).
@@ % IS = zw(124).

Hier kann der Fakt auch zwischendurch als alternative Regel verwendet werden, allerdings wird er das erst wenn die rekursive Regel scheitert. Dadurch wird der Suchbaum in die andere Richtung durchlaufen und die Ergebnisse anders rum aufgezählt.

Programmtransformation vs. Compiler[Bearbeiten | Quelltext bearbeiten]

Compiler übersetzen das Programm ohne auf deren Semantik genauer einzugehen. Es gibt höchstens lokale Optimierungen, denn globale Optimierungen könnten nicht deterministisch sein, denn Compiler dürfen das Verhalten des Programms keinesfalls verändern. Gar Endlosschleifen werden erst gar nicht weg-optimiert, da dies das Verhalten verändert. Die Zielsprache ist meist nicht die selbe wie die Quellsprache, aber die Semantik ist "Äquivalent".

Programmtransformationen betrachten auch das globale Verhalten und versuchen Optimierungen vorzunehmen, die auch heuristisch, optimistisch sein können. Dadurch wird das Verhalten ebenfalls verändert, wie etwa Endlosschleifen treten eventuell nicht mehr auf. Dazu werden Strategien verwendet. Unter der Voraussetzung, dass die Regeln korrekt sind können die Strategien für sich keine inkorrekten Lösungen mehr erzeugen. Im schlimmsten Fall terminiert die Transformation nicht oder liefert keine Verbesserung.

Im Allgemeinen ist es aber nicht wünschenswert, dass Programmtransformationen Endlosableitungen beseitigen. Die Möglichkeit das zu tun, kann von leicht veränderbaren Parametern abhängen und bereits nach einer winzigen Programmänderung nicht mehr gegeben sein. Es könnte also sein, dass nach einer kleinen scheinbar unbedeutenden Änderung am Programm Endlosableitungen zutage treten.

Direkter Vergleich zw. Compiler und Programmtransformationssystemen:

Compiler Programmtransformationssystem

übersetzt Hochsprache in maschinennähere Sprache

übersetzt meist in gleich Sprache

konzeptueller Aufbau: passorientiert

Programmtext -> AST -> Zwischencode --> Maschinencode

konzeptueller Aufbau:

  • wenige Regeln (beschreiben mögliche Äquivalenzumformungen)
  • viele Strategien (steuern Regelanwendung, können Korrektheit nicht beeinflussen)

Algorithmen = Regeln + Strategie

Fehler können überall auftreten (in jedem Schritt)

Fehler können nur in den Regeln auftreten. Sollte eine Strategie fehlerhaft sein, so kann das Programm langsamer werden, oder unter Umständen sogar gar nicht terminieren, aber die Äquivalenz bleibt immer erhalten.

sehr lokale Optimierung

betrachtet das gesamte Programm (und nicht etwa nur einzelne Regeln)

automatisch, keine Interaktion

oft interaktiv, Annotationen, Heuristiken

Aufbau eines Transformationssystems[Bearbeiten | Quelltext bearbeiten]

Arten von Äquivalenz[Bearbeiten | Quelltext bearbeiten]

  • Deklarativ - Aussage unverändert
  • Prozedural - Prozedurale Eigenschaften Prologs - zB. Endlosableitung - bleiben erhalten.

siehe Folien, Seite 31.

Allg. Kriterien für Transformationssysteme[Bearbeiten | Quelltext bearbeiten]

fold/unfold[Bearbeiten | Quelltext bearbeiten]

unfold ; "Ersetzen eines Ziels durch entsprechende Definition."
a( A, B) :-
   x(A),
   y(B).

:- a( 1, 2). % unfold
:- x(1),
   y(2).
fold; Umkehrung.
b( Kind, Person) :-
   kind_von( Kind, X),
   kind_von( X, Person).

:- kind_von( A, M),
   kind_von( M, ich). % fold
:- b( A, ich).

Was ist eine Eureka-Definition?[Bearbeiten | Quelltext bearbeiten]

Eine Definition, die nur Ziele verwendet, die anderswo verwendet werden - fasst diese zusammen. Definition darf nur aus einer Regel bestehen.

Laut Folien:

  • "Ziele mit gemeinsamer existentieller Variable". Es dürfen auch mehrere Variablen verwendet werden, aber die verwendeten Ziele müssen mit zumindest einer Variable zusammenhängen.
  • "generalisiertes Ziel"

Programme wie aus ^^appendnachsuffix vorrechnen[Bearbeiten | Quelltext bearbeiten]

Partielle Evaluation[Bearbeiten | Quelltext bearbeiten]

Schon im Vorraus werden Prädikate resuliert werden, wie etwa:

maennlich( fridolin).
maennlich( georg).
maennlich( hans).

weiblich( inge).
weiblich( lena).
weiblich( ara).

hamster( fridolin).

vogel( ara).

mensch( georg).
mensch( hans).
mensch( inge).
mensch( lena).

:- pe mann(P).
mann( P) :-
  mensch( P),
  maennlich( P).

:- pe frau(P).
frau( P) :-
  mensch( P),
  weiblich( P).

mann und frau werden optimiert:

mann( georg).
mann( hans).

frau( inge).
frau( lena).

Nun kann bereits der Regelkopf verwendet werden, statt den Zielen im Regelkörper auswerten zu müssen, um ein Ziel auswerten zu können.

Schrittweise Spezialisierungen werden durch die Futamura-Projektionen formal beschrieben:

1. Projektion[Bearbeiten | Quelltext bearbeiten]

Liegt ein Programm in seinem Quellcode vor, so benötigt es einen Interpreter um aus Quellcode und Eingabe eine Ausgabe zu erzeugen:

 ausgabe = interpreter(quellcode, eingabe) 

Die erste Projektion besagt jetzt, dass eine Spezialisierung (mix) des Interpreters auf den Quellcode das Zielprogramm hervorbringt:

programm = mix(interpreter, quellcode)
ausgabe = programm(eingabe)

Der Quellcode wurde sozusagen in den Interpreter hineingebacken.

2. Projektion[Bearbeiten | Quelltext bearbeiten]

Die zweite Projektion besagt, dass eine Spezialisierung des Spezialisierers auf den Interpreter einen Compiler hervorbringt:

compiler = mix(mix, interpreter)
programm = compiler(quellcode)
ausgabe = programm(eingabe)

Der Interpreter wurde sozusagen in den Spezialisierer hineingebacken.

3. Projektion[Bearbeiten | Quelltext bearbeiten]

Die dritte Projektion besagt, dass eine Spezialisierung des Sepzialisierers auf den Spezialisierer selbst einen Compiler-Generator hervorbringt:

compilergenerator = mix(mix, mix),
compiler = compilergenerator(interpreter)
programm = compiler(quellcode)
ausgabe = programm(eingabe)

(Typen)[Bearbeiten | Quelltext bearbeiten]

(CLP Schema)[Bearbeiten | Quelltext bearbeiten]

Gleichungslöser für ganze, rationale, irrationale Zahlen und Boolsche Werte.

Statt is/2 wird #op/2 verwendet, wobei op für =, >, <, <=, => \= stehen kann., wodurch auch Ungleichungen gelöst werden können.

(Konsistenztechniken vs. Unifikation)[Bearbeiten | Quelltext bearbeiten]

(CLP(FD))[Bearbeiten | Quelltext bearbeiten]

Final Domains - Gleichungslöser mit ganzen Zahlen.

Relationen ; #<, #>, #\=, #=, #=<, #>=, in
Arith. Ausdr. ; +,-,*,/,mod,min,max,abs
labeling ; ff, ffc
  • Scheinlösungen
  • Labeling

LAMBDAs in Prolog (neu)[Bearbeiten | Quelltext bearbeiten]

Lambdas bestehen aus 3 Prädikaten:

\ ; Lambda, wenn man keine globalen Variablen benötigt.
+\ ; Lambda mit globalen Variablen.
^ ; Lokale Variablen, die auch als Argumente dienen, verdecken.

Beispiel mit maplist:

:- C=a, D=b, Ls=[b,1,3,5], Ks=[4,a,3,b]
   maplist( [C,D]+\A^B^( dif(A,B), dif(A, C), dif( B, D) ), Ls, Ks)

Es werden dabei folgende Vergleiche angestellt:

:- dif(b,4), dif(b,a), dif(4,b),
   dif(1,a), dif(1,a), dif(1,b),
   dif(3,3), dif(3,a), dif(3,b),
   dif(5,b), dif(5,a), dif(b,b).

Strukturen umwandeln:

:- C=9, maplist( C+\A^B^( A=abc(X-X), B=x(X, C) ), [abc(a-a), abc(b-b), abc([]-[]), abc(1-1), abc(m(9)-m(9))], Xs).
% Xs = [x(a,9), x(b,9), x([],9), x(1,9), x(m(9),9)].

Umbenennung[Bearbeiten | Quelltext bearbeiten]

Ich bin mir nicht sicher, ob diese Erklärung stimmt, aber so habe ich es jedenfalls verstanden:

:- maplist(Y^(Y#>=3), [X1, X2]).
% X1 = Y, X2 = Y, Y in 3..sup

Da hier KEINE Umbenennung stattfindet (\ fehlt), wird für jedes Listenelement das exakt gleiche Prädikat verwendet (Y^(Y#>=3)). Informell entspricht diese Anfrage folgendem:

^(V1, Goal, V1) :- call(Goal).

:- ^(Y, #>=(Y, 3), X1), ^(Y, #>=(Y, 3), X2).

Aus der Definition des ^ Prädikat folgt eine Unifikation von Y mit dem übergebenen Argument. Y muss also gleich mit X1 sein (durch die Verarbeitung des ersten Listenelements) und Y muss gleich mit X2 sein (durch die Verarbeitung des zweiten Listenelements) und zudem muss natürlich die beschrieben Ungleichung gelten.

Das ganze mit Umbenennung:

:- maplist(\Y^(Y#>=3), [X1, X2]).
% X1 in 3..sup, X2 in 3..sup

Diesmals passiert folgendes: jedes mal wenn ein Listenelement mit dem Prädikat \Y^(Y#>=3) geprüft wird, führt das copy_term aus der Definition von \ dazu, dass stattdessen ein Prädikat mit frischen Variablen verwendet wird. Informell entspricht die Ausführung folgendem:

^(V1, Goal, V1) :- call(Goal).

:- ^(_A, #>=(_A, 3), X1), ^(_B, #>=(_B, 3), X2).

Beispiel für Lambdas und Call[Bearbeiten | Quelltext bearbeiten]

Diese Beispiel bedarf einer Erklärung und ist außerdem nicht vollständig - bitte vervollständigen.

:- X=2,X^(Y#=X+1).
:- X=2,call(Z^(Y#=Z+1),X).
:- X=2,Exp=Z^Y#=Z+1,call(Exp,X).

Beispiel für Lambdas und Maplist[Bearbeiten | Quelltext bearbeiten]

ist_matrix/1 kann man entweder mit zwei Regeln oder mit Lambdas definieren. Hierzu das Beispiel:

ist_matrix([Xs]) :-
  phrase(liste(Xs),_).
ist_matrix([Xs,Ys|Xss]) :-
  Xs = [_|_],
  liste_gleichlang(Xs,Ys),
  ist_matrix([Ys|Xss]).

oder

ist_matrix2(Xs) :-
  maplist(Listlength+\X^(is_list(X),list_length(X,Listlength)),Xs).

siehe Lambdas in ISO Prolog