Prolog PROgramming in LOGic LPA Prolog Ambiente Windows a 32-bit .pl .exe Interprete e non compilatore Sintassi di Edimburgo Dati o Termini semplici costanti atomi strutturati variabili numeri Termini semplici Costanti Atomi 1. 3. 4. 2. Sono Un Una Unacarattere stringa stringa atomi anche: di di minuscolo caratteri caratteri tra speciali: seguito da ! ^;< > []: . {} altri+caratteri: singole - / virgolette: \= @ :#$& 'ABC' <> a carloMagno '1234' ##&& due_luglio_2000 'a<>b' ::= Termini semplici Costanti Numeri 1. Reali: Interi: 0.5 0 -3.1416 -16 6.23e+23 33 11.0e-3 +100 -2.6e-2 Termini semplici Costanti Variabili Tutte le variabili in Prolog iniziano con una lettera maiuscola o con _ X25 List Massimo_De_Gregorio _234 Termini strutturati Una termine strutturato può essere formato da diversi componenti. Ogni componente di una struttura può essere un termine (semplice o strutturato). Struttura Liste - rappresentazione un generale caso particolare reale di termini strutturati Unatermine Un lista in strutturato Prolog è una è sintatticamente collezione di termini. formato da un sole] •funtore la lista e una vuota lista è rappresentata di [mare, argomenti. da In particolare: [ ]; [mela, • iluna funtore lista deve non vuota essere è specificata unpera, atomo;uva] scrivendo i suoi • la elementi lista di argomenti tra parentesi è racchiusa quadre e divisi tra parentesi; da ",". .(sole, [])) [].(mare, [mela, uva] da una ","; • ogni argomento è[a] separato dalpera, successivo .(mela,è un .(pera, • ogni argomento termine..(uva, []))) padre(mario,carlo) f(a,g(A,c),h(d)) per(2,3,6) Corrispondenza tra termini e alberi • La radice dell'albero è il funtore; • ogni argomento della struttura è un figlio della radice; • se un argomento è una struttura, sarà disegnato usando quest'algoritmo ricorsivamente. Corrispondenza tra termini e alberi somma(2, 3, 5) • somma 2 3 [mela, pera, uva] mela 5 • • pera uva [] Componenti di un programma • Un programma Prolog è una collezione di predicati (o regole); • un predicato stabilisce una relazione tra gli oggetti; • in Prolog, l'esecuzione di un programma significa che una query è data e il sistema cerca di stabilire se la query segue (logicamente) dai predicati del programma. Predicati e Clausole • Un predicato è composto da una o più clausole • Un goal è un termine definito dal programmatore ed è o un atomo o un termine strutturato • La forma generale di una clausola è la seguente: <left-hand-side> :- <right-hand-side>. grand_parent(X, Y) :parent(X, Z), parent(Z, Y). una clausola parent(X, Y) :- mother(X, Y). parent(X, Y) :- father(X, Y). due clausole Clausole unitarie o Fatti 'Mario' è il padre di 'Alberto' 1 - father('Mario', 'Alberto') :- true. 2 - father('Mario', 'Alberto'). Queries Chi è il padre di 'Alberto'? ?- father(X, 'Alberto'). X = 'Mario' ?- Scrivere programmi • • • • Un programma Prolog è un insieme di fatti e regole Fatti e regole sono memorizzati in uno o più file Formato libero Commenti % <commento> /* <commento1> <commento2> */ • Formattazione del codice grand_parent(X, Y) :parent(X, Z), parent(Z, Y). parent(X, Y) :- mother(X, Y). parent(X, Y) :- father(X, Y). Finestre Main e Console La console Comandi da console • uso dell'<enter> • editing dei comandi • riscrittura dei comandi • comandi su più linee <ctrl-enter> e <enter> • opzione break in - <ctrl-break> Azzeramento della console Output sulla console Le variabili Prolog C o Pascal singolo data object locazione di memoria (valore mantenuto) (diversi valori) (fino alla fine) (durante) Variabili che appaiono inizialmente nel goal nella parte sinistra della regola sono quantificate universalmente. Variabili che appaiono nel goal solo nella parte destra della regola sono quantificate esistenzialmente (lo stesso per quelle che appaiono nelle query). Semantica dei programmi Prolog Dichiarativo Procedurale what how (quale sarà il risultato) (come trovare il risultato) grand_parent(X, Y) :parent(X, Z), parent(Z, Y). ?- grand_parent(mario, Y). ?- grand_parent(mario, carlo). Unificazione T1 e T2 sono due termini costanti sono lo stesso termine variabili uno alias dell’altro T1 variabile e T2 qualsiasi termine T1 istanziato a T2 T1 e T2 termini strutturati stesso funtore stessa arità tutti gli argomenti corrispondenti essere unificati Esecuzione di un programma in Prolog Verifica di una collezione di goal rispetto a un dato programma La verifica è fatta in accordo all'Algoritmo di Risoluzione (Prolog engine o Resolution mechanism) L'input a questo algoritmo è un programma (collezione di predicati) e una query (uno o più goal) L'output sarà successo o fallimento a seconda se la validità della query iniziale potrà essere verificata o meno Nel caso la query contenga delle variabili e possa essere verificata, l'algoritmo mostrerà particolari valori delle variabili che rendono la query valida Esecuzione di un programma in Prolog | ?- father(albert, john). | ?- sister(paul, X). | ?- sister(paul, X), mother(X, Y). Esecuzione di un programma in Prolog Possiamo vedere l'esecuzione di un programma Prolog come un problema di ricerca e la rappresentazione del trace come un albero. L'obiettivo, quindi, dell'algoritmo è di trovare una soluzione (se esiste) nello spazio di ricerca. Depth-first e backtracking Poiché i predicati possono essere ricorsivi, una ricerca breadth-first avrebbe il vantaggio di trovare sempre una soluzione se esiste. D'altro canto, una ricerca depth-first dello stesso albero potrebbe non terminare quando, per esempio, le clausole ricorsive sono provate per prime. Controllo del backtracking Il backtracking agisce automaticamente sul fallimento per soddisfare un goal. Sebbene questa operazione è essenziale per la procedura di ricerca usata dal Prolog, essa può anche portare a delle inefficienze (ad esempio, trovare soluzioni alternative a un goal che può essere soddisfatto solo una volta). In questi casi, sarebbe molto utile ai programmatori indicare che il backtracking non deve agire. Il Prolog rende disponibile un meccanismo per il controllo del backtracking: il cut. Controllo del backtracking Il cut è un goal. Esso ha successo immediatamente appena si incontra in una clausola. Però, facendo ciò, rimuove tutti i punti di backtracking tra l'inizio del predicato e se stesso. q(X, Y) :- a(X), b(Y). q(X, Y) :- c(X), !, d(Y). q(X, Y) :- e(X, Y). | ?- …, p(A), q(A, B), r(B), … Controllo del backtracking Green cut Predicati definiti da più clausole in cui esattamente una sola clausola può avere successo (mutuamente esclusive). Dopo Prima max(A, max(A,B,B,A)A):-:-A A>=>=B,B.!. max(A, max(A,B,B,A)A):-:-B B>=>=A,A.!. Controllo del backtracking Red cut Quando il cut è introdotto per evitare o saltare la verifica di un goal. La semantica del programma è alterata (non produce lo stesso risultato con e senza cut). Prima Dopo add(X,L,L) :member(X,L). !. member(X,L), add(X,L,[X|L]). | L L ?- add(a,[a,b,c],L). = [a,b,c] ; = [a,a,b,c] Controllo del backtracking q(X, Y) :- a(X), b(Y). q(X, Y) :- c(X), !, fail. Controllo del backtracking Un altro predicato di controllo IF-Then-Else Modifica dinamica dei programmi Programmi che aggiungono e rimuovono clausole in questo modo introducono effetti I programmi, una volta caricati - consult - in collaterali: la astatici. loro Una esecuzione causa Prolog, volta che un Il Prolog sono mette disposizione predicati il cui cambiamenti chee possono future insieme fatti regole è modificare stato caricato in scopo è di aggiungere o rimuovere clausole. esecuzioni (chiamate che hanno successo Prolog, questo insieme non può essere Una volta modificato in questo modo, la in un originale determinato punto possono, in modificato durante l'esecuzione forma del predicato è persa edel la seguito, fallire aver successo un numero programma (l'unica eccezione, nuova forma èo mantenuta finoovviamente, a ulteriori differente di volte). è un nuovo caricamento del programma modifiche. Programmi reconsult). che adottano queste tecniche sono estremamente difficili da seguire, modificare e da inspezionare (debug). Modifica dinamica dei programmi assert(Clause) aggiunge una clausola clausole di un predicato alla fine delle assert( (p(X) :- q(X, Y), r(Y)) ). assert( p(pippo) ). assert( p(_) ). p(X) :- q(X, Y), r(Y). p(pippo). p(_). Modifica dinamica dei programmi asserta(Clause) aggiunge una clausola clausole di un predicato all'inizio delle assert( (p(X) :- q(X, Y), r(Y)) ). asserta( p(pippo) ). assert( p(_) ). p(pippo). p(X) :- q(X, Y), r(Y). p(_). Modifica dinamica dei programmi assertz(Clause) aggiunge una clausola clausole di un predicato alla fine delle assert( (p(X) :- q(X, Y), r(Y)) ). assertz( p(pippo) ). assert( p(_) ). p(X) :- q(X, Y), r(Y). p(pippo). p(_). Modifica dinamica dei programmi retract(Clause) rimuove una clausola di un predicato assert( (p(X) :- q(X, Y), r(Y)) ). assert( p(pippo) ). assert( p(_), 2 ) . retract( p(pippo) ). p(X) :- q(X, Y), r(Y). p(_). Modifica dinamica dei programmi retractall(Head) rimuove tutte le clausole la cui testa è Head assert( (p(X) :assert( p(pippo) retractall( p(_) assert( p(pluto) p(pluto). q(X, Y), r(Y)) ). ). ). ). Modifica dinamica dei programmi Per poter manipolare i predicati usando assert e retract i predicati devono essere dinamici. I predicati creati a runtime sono dinamici. Quando i predicati sono consultati da un file, per poterli modificare devono essere resi dinamici esplicitamente. :- dynamic fratello/2, append/3. Modifica dinamica dei programmi Cosa succede se eseguiamo la seguente query? | ?- retract(X), fail. La query ovviamente fallisce ma l'effetto collaterale è quello di rimuovere tutte le clausole dinamiche dal programma. findall e forall forall(robot(_,_,_,_,Robot), muovi_robot(W,N,Robot)) findall(N,neuron(N,_,_,_,_,in),In) Ricorsione write_list([]). write_list([A|L]) :write(A), nl, write_list(L). somma([],0). somma([A|L],S) :somma(L,S1), S is A+S1. Robot spazzino :- dynamic robot/3, sporco/2. sporco(5,5). sporco(2,8). sporco(3,7). sporco(7,1). sporco(1,1). sporco(4,9). sporco(9,2). robot(0,0,0). cestino(0,0). vai :sporco(X,Y), robot(X,Y,0), raccogli, vai. vai :sporco(X,Y), robot(X1,Y1,0), verso(X,Y), vai. vai :cestino(X,Y), robot(X,Y,1), svuota, vai. vai :cestino(X,Y), robot(X1,Y1,1), verso(X,Y), vai. vai. Robot spazzino raccogli :robot(X,Y,0), retract(sporco(X,Y)), retractall(robot(_,_,_)), write('Immodizia raccolta!'), nl, assert(robot(X,Y,1)). svuota :robot(X,Y,1), retractall(robot(_,_,_)), write('Serbatoio svuotato!'), nl, assert(robot(X,Y,0)). Robot spazzino verso(X,Y) :robot(X1,Y1,S), Dx is X-X1, Dy is Y-Y1, muoviti(Dx,Dy,DX,DY), retract(robot(X1,Y1,S)), X2 is X1+DX, Y2 is Y1+DY, write('Robot in: '), write(X2), write(', '), write(Y2), nl, assert(robot(X2,Y2,S)). muoviti(0,Dy,0,DY) :DY is Dy/abs(Dy). muoviti(Dx,0,DX,0) :DX is Dx/abs(Dx). muoviti(Dx,Dy,DX,DY) :DY is Dy/abs(Dy), DX is Dx/abs(Dx).