Künstliche Intelligenz und Prolog
Copyright © 2020 Jiri Kriz, www.nosco.ch

5Eingebaute Prädikate

Eingebaute Prädikate können klassifiziert werden in:

Eingebaute Prädikate hängen von der jeweiligen Prolog-Implementation ab.

Prolog und Logik

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ädikatOutput-PrädikatAktion
see( File)tell( File)File wird der current Stream.
seeing( File)telling( File)Ist File der current Stream?
seentoldDer current Stream wird ge­schlossen,
'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
nlnew 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:

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 =.. LL 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.

listingschreibt 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).