5Eingebaute Prädikate
Eingebaute Prädikate können klassifiziert werden in:
- extra-logische: sie haben keine Entsprechung in der Logik, z.B. Cut, read, write.
- meta-logische: sie gehören einer Logik höherer Ordnung an, z.B. call, univ.
Eingebaute Prädikate hängen von der jeweiligen Prolog-Implementation ab.
Fig. 5.1: Prolog und Logik
5.1Input / Output
Input und Output geschehen über sequentielle Streams (Files). Ein File kann entweder als ein Input- oder ein Output-Stream verwendet werden. Der Input-Stream von der Tastatur heisst 'user', der Output-Stream zum Bildschirm heisst auch 'user'. Es gibt immer genau einen mo-mentanen (current) Input- und Output Stream.
File ist eine Variable, die mit dem Filenamen (ein Atom) unifiziert.
Input-Prädikat | Output-Prädikat | Aktion |
---|---|---|
see( File) | tell( File) | File wird der current Stream. |
seeing( File) | telling( File) | Ist File der current Stream? |
seen | told | Der current Stream wird geschlossen, 'user' wird der neue current Stream |
Operationen auf den current Streams (ergeben Failure bei Backtracking):
read( T) | lese den nächsten Term und unifiziere ihn mit T |
read( eof) | End of File |
write( T) | schreibe den Term T |
nl | new Line |
tab( N) | schreibe N Blanks |
put( C) | schreibe den ASCII Character C, 0 =< C < 256 |
get( C) | lese den nächsten druckbaren Character und unifiziere
ihn mit C, 32 =< C < 256 |
get0( C) | lese den nächsten Character und unifiziere ihn mit C, 0 =< C < 256 |
display( T) | schreibe die Struktur des Terms T |
Beispiel: Verarbeitung eines Files von Prolog Termen
File1 | |
read | |
Term1 | |
process | |
Term2 | |
write | |
File2 |
read | process | write | ||||
File1 | Term1 | Term2 | File2 |
Rekursive Implementierung (nur bei Tail Recursion Optimierung):
process_file( File1, File2) :-
see( File1), tell( File2),
process_open_files(File1, File2),
seen, told.
process_open_files( File1, File2) :-
read( Term1),
process_term( Term1, Term2),
write_term( Term2),
Term1 \== eof,
process_open_files( File1, File2).
process_open_files( File1, File2) :- !.
process_term( eof, eof) :- !.
process_term( Term1, Term2) :-
transform( Term1, Term2), !.
write_term( eof) :- !.
write_term( Term) :-
write( Term), write( '.'), nl, !.
Iterative Implementierung (Failure-Driven Loop) :
process_open_file( File1, File2) :-
repeat,
read( Term1),
process_term( Term1, Term2),
write_term( Term2),
Term1 == eof, !.
repeat ist vordefiniert und ergibt immer Success, auch bei Backtracking. Die Abbruchbedingung ist Term1 == eof. Falls diese Bedingung nicht erfüllt ist, wird REDO an
write_term( Term2), process_term( Term1, Term2), read( Term1).
durchgeführt, und ergibt überall Failure. REDO von repeat ergibt Success und die Schleife wird wiederholt. Es muss garantiert werden, dass alle Subgoals zwischen repeat und der Abbruchbedingung nicht backtrackable sind (darum überall Cuts!). Sobald die Abbruchbedingung erfüllt wird, wird der Loop unwiderruflich verlassen (garantiert durch ein Cut!).
Praktische Bemerkungen:
- read( eof) ergibt immer success am Ende des Files
- File-Names müssen Pfade enthalten, z.B. 'User:file1'.
- Wenn das Programm die Streams nicht schliesst, so bleiben sie geöffnet mit der bestehenden File-Position. Beim Wiederstart des Programms entstehen dann Fehler.
5.2Behandlung von Termen
Klassifikation
var( X)
X ist eine nicht instantiierte Variable
(Sharen gilt nicht als Instantiieren)
atom( A)
A ist ein Atom (aber keine Zahl), s. Kapitel 2.5
atomic( C)
C ist eine Konstante (Atom oder Zahl oder String)
var( X) | X ist eine nicht instantiierte Variable (Sharen gilt nicht als Instantiieren) |
atom( A) | A ist ein Atom (aber keine Zahl), s. Kapitel 2.5 |
atomic( C) | C ist eine Konstante (Atom oder Zahl oder String) |
Manipulation
name( A, L)
L ist die Liste der ASCII Zeichen, die das Atom A bilden.
?- name( X, [ 97, 105] => X = ai
?- name( ab, L) => L = [ 97, 98]
functor( T, F, A)
T ist ein Term mit Funktor F und Arität A.
?- functor( f( a, X), F, A) => F = f, A = 2
arg( N, T, A)
Das N-te Argument des Termes T ist A.
?- arg( 2, f( X, a( 1), b), A) => A = a( 1)
T =.. L
L ist eine Liste, ihr Head ist der Funktor von T ist, und ihr Tail ist die Liste der Argumente von T ist. Der Operator '=..' heisst aus historischen Gründen "univ" (für "universal").
?- f( a, b) =.. L => L = [ f, a, b]
?- T =.. [ schnell, auto] => T = schnell( auto)
name( A, L) | L ist die Liste der ASCII Zeichen, die das Atom A bilden. ?- name( X, [ 97, 105] => X = ai ?- name( ab, L) => L = [ 97, 98] |
functor( T, F, A) | T ist ein Term mit Funktor F und Arität A. ?- functor( f( a, X), F, A) => F = f, A = 2 |
arg( N, T, A) | Das N-te Argument des Termes T ist A. ?- arg( 2, f( X, a( 1), b), A) => A = a( 1) |
T =.. L | L ist eine Liste, ihr Head ist der Funktor von T ist, und ihr Tail ist die Liste der Argumente von T ist. Der Operator '=..' heisst aus historischen Gründen "univ" (für "universal"). ?- f( a, b) =.. L => L = [ f, a, b] ?- T =.. [ schnell, auto] => T = schnell( auto) |
Beispiele:
?- functor( D, datum, 3),
arg( 1, D, 4),
arg( 2, D, dez),
arg( 3, D, 1990).
⇒ D = datum( 4, dez, 1990)
map( _, [], []).
map( F, [ X1| Xs], [ Y1| Ys]) :-
Q =.. [ F, X1, Y1], call( Q), /* Anwendung von "F( X1, Y1)" */
map( F, Xs, Ys).
square( X, Y) :- Y is X * X.
?- map( square, [ 1, 2], Y). ⇒ Y = [ 1, 4]
5.3Manipulation der Prolog Datenbank
Ein Fakt ist semantisch ein Prädikat; syntaktisch ist er ein Term. Eine Regel ist semantisch eine Implikation; syntaktisch ist sie ein Term mit dem Funktor :- der Arität 2, dem ersten Argumenten gleich dem Head und dem zweiten Argumenten gleich dem Body. Z.B. A :- A1, A2, A3 ist der Term ':-'( A, (A1, A2, A3)). Das erste Komma in diesem Term ist ein syntaktisches Zeichen, das Argumente eines Terms trennt. Die Kommas zwischen A1 und A2, A2 und A3, sind der Infix-Operator der Konjuktion. Die Klammer um A1, A2, A3 ist also notwendig.
listing | schreibt die ganze Prolog Datenbank. |
listing( P) | schreibt alle Klauseln, deren Head den Funktor P hat. |
clause( H, B) | Die Klausel H :- B ist in der Datenbank. H muss beim Aufruf so instantiiert sein, dass der Funktor und die Arität bekannt sind. Body B eines Faktes gibt true. ?- clause( member( _, _), B) => B = true; B = member( _, _); no. clause ist backtrackable. |
assert( C) | fügt den Term C als eine Klausel in die Datenbank ein ?- assert( ( a( X) :- b( X), c( X) )). assert ist nicht backtrackable. |
asserta( C) | fügt den Term C als eine Klausel am Anfang der Datenbank ein |
assertz( C) | fügt den Term C als eine Klausel am Ende der Datenbank ein |
retract( C) | entfernt die Klausel C aus der Datenbank. retract ist backtrackable. |
Beispiel: Entferne alle Klauseln mit dem gegeben Prädikatennamen (durch Backtracking):
retractall( Fact) :-
retract( Fact), fail.
retractall( Head) :-
retract( Head :- Body),
fail.
retractall( _). /* catch all => Success */
Beispiel: Prolog-Meta-Interpreter in Prolog
solve( true).
solve( H) :-
clause( H, B),
solve( B).
solve( ( G1, G2)) :-
solve( G1),
solve( G2).