?- persona(X).X = paolo Yes X = giulio Yes X = luca Yes No
persona sono stati
immessi nel database. (L'ordine con cui i fatti erano scritti
nel file, nella "text area" nel nostro caso, che è stato consultato.
In risposta al goal:
?- trace,uomo(francesca).
si ottiene:
Call: ( 15) uomo(francesca) ? Call: ( 16) persona(francesca) ? Call: ( 17) maschio(francesca) ? Fail: ( 17) maschio(francesca) ? Redo: ( 16) persona(francesca) ? Call: ( 17) femmina(francesca) ? Exit: ( 17) femmina(francesca) ? Exit: ( 16) persona(francesca) ? Exit: ( 15) uomo(francesca) ?
Tenedo presente che fatti e regole erano stati così numerati:
maschio(paolo). /* fatto 1 */
femmina(francesca). /* fatto 2 */
uomo(X) :- /* regola 1 */
persona(X).
persona(Y) :- /* regola 2 */
maschio(Y).
persona(Z) :- /* regola 3 */
femmina(Z).
Si vede che per rispondere all'interrogazione uomo(francesca)
si utilizza la regola 1. Il nuovo goal diventa persona(francesca).
Si cerca una soluzione usando la regola 2 che genera il goal
maschio(francesca). Ma questo goal fallisce innescando
il meccanismo di backtracking. A questo punto si torna a considerare il
goal persona(francesca) ma si cerca di risolverlo con la regola 3
per persona. questa regola genera il goal femmina(francesca)
che ha successo per via del fatto 2. Il goal iniziale risulta così
soddisfatto.
Cut
la definizione:
fact(0,1). fact(N,X):- M is N-1,fact(M,Y),X is Y*N.
Non funziona correttamente. Se si sottomette al Prolog la domanda
?- fact(0,X)si ottiene come prima risposta X=1 in quanto viene utilizzato il fatto. Tuttavia se si forza il backtrackig, invece del fatto si utilizza la regola viene così generato un nuovo goal
?- fact(-1,X)e, dopo una ulteriore utilizzazione della stessa regola,
?- fact(-2,X)
Si cade quindi in un loop che non ha termine.
Anche se si chiede di calcolare il fattoriale di un intero positivo,
si ottiene una prima risposta tramite il fatto ?-fact(0,X)
che mette fine alle chiamate ricorsive,
ma una richiesta di backtracking manda in loop l'interprete.
Possiamo schematizzare i goal generati dalle chiamate ricorsive
per la domanda
?- fact(2,X)in questo modo:
-------------
| fact(2,X) |
-------------
|
V
-------------
| fact(1,X) |
-------------
|
V
-------------
| fact(0,X) |
-------------
| \
V \
Termina \
dando la \
prima |
soluzione V
--------------
| fact(-1,X) |
- -------------
|
V
--------------
| fact(-2,X) |
--------------
|
V
.......
eta(P,_),puo_lavorare(P).P = Gatti Yes P = Fabbri Yes P = Ferrari Yes No
puo_lavorare(X):-eta(X,Y),Y<30,!,fail. puo_lavorare(X):-laureato(X),militesente(X), eta(X,Y),Y >= 30,Y<40. .....
dividi(N1, N2, Risultato) :- intero(Risultato), Prod1 is Risultato * N2, Prod2 is (Risultato + 1) * N2, Prod1 =< N1, Prod2 > N1, !. intero(0). intero(N) :- intero(N1), N is N1 + 1.
Rimuovendo il cut alla fine della regola dividi, se si forza il backtracking
dopo aver trovato la prima (e unica) soluzione, intero
genera il numero successivo che però non soddisfa
la condizione Prod1 =< N1. Il fallimento di questa condizione
fa generare a intero un nuovo numero, e il processo non termina.
/* appartenenza ad una lista - non deterministico */ membro(X,[X|_]). membro(X,[_|Y]) :- membro(X,Y). /* appartenenza ad una lista - deterministico*/ membro1(X,[X|_]) :- !. membro1(X,[_|C]) :- membro1(X,C).Posssiamo confrontare goal in cui ambedue gli argomenti sono istanziati.
?- membro(y,[a,y,b,y]). Yes Yes No ?- membro1(y,[a,y,b,y]). Yes NoIn questo caso si può notare che la prina versione non deterministica trova due soluzioni, in quanto
y compare 2 volte nella liste
che compare come secondo
argomento. Nel caso della versione deterministica il cut fa si che
si veda solo la prima soluzione.
?- membro(X,[a,b,c,d]). X = a Yes X = b Yes X = c Yes X = d Yes No ?- membro1(X,[a,b,c,d]). X = a Yes NoSe invece usiamo membro con il secondo argomento variabile si ha:
?- membro(a,L). con "solution=once" L = [a|_G2525] Yes ?- membro(a,L). va in loop se si usa "solution=all" ?- membro1(a,L). con "solution=all" L = [a|_G2092] Yes NoNel primo caso
membro va in loop in quanto si usa "solutions=all"
infatti ci sono infinite soluzioni. a può essere il primo, il secondo
il terzo ..... elemento della lista L.
Con il cut ci si ferma alla prima soluzione.
/* concatena due liste */ conc([],L,L). conc([X|L1],L2,[X|L3]) :- conc(L1,L2,L3). /* concatena due liste - usa il cut*/ conc1([],L,L):- !. conc1([X|L1],L2,[X|L3]) :- conc1(L1,L2,L3).Puoi confrontare queste due versioni sfruttando questi goal (usare "solutions=all"):
?- conc([a,b,c],[e,f],L). ?- conc1([a,b,c],[e,f],L).in questo caso le risposte coincidono. L'unica soluzione è
L=[a,b,c,d,e,f].
Anche non istanziando il secondo argomento
?- conc([a,b,c],X,L). ?- conc1([a,b,c],X,L).si ottengono risposte coincidenti:
X=_G3757,L=[a,b,c|_G3757].
conc
non e' istanziato. In questo caso il goal
?- conc(X,[a,b,c],L). /* attenzione questo va in loop */manda in loop l'interprete, mentre il goal
?- conc1(X,[a,b,c],L).Produce come risposta:
X = [] L = [a, b, c] Yes NoIstanziando solo l'ultimo argomento,
conc produce tutte
le possibili suddivisioni della lista che compare come terzo argomento.
?- conc(X,Y,[a,b,c]). conc(X,Y,[a,b,c]). X = [] Y = [a, b, c] Yes X = [a] Y = [b, c] Yes X = [a, b] Y = [c] Yes X = [a, b, c] Y = [] Yes NoAl contrario,
conc1, a causa del cut nella proma clausola,
da solamente la prima risposta.
?- conc1(X,Y,[a,b,c]). X = [] Y = [a, b, c] Yes No
intersezione([],_Y,[]).questa prima clausola dice semplicenente che l'intesezione di un insieme vuoto con un secondo insieme deve dare quest'ultimo come risultato.
intersezione([X|R],Y,[X|Z]) :- appartiene(X,Y), !, intersezione(R,Y,Z).In questa clausola si dice che se il primo insieme è dato da un primo elemento
X seguito da altri elementi R
e X appartiene all'insieme Y, X deve
essere aggiunto all'intersezione
Z fra R e Y.
intersezione([_X|R],Y,Z) :- intersezione(R,Y,Z).Per effetto del cut si arriva a questa clausola solo se nella clausola precedente è fallito
appartiene(X,Y).
In questo caso l'elemento X non appartiene
all' insieme Y e non deve essere inserito
nel risultato.
Un ragionamento analogo vale per il predicato unione.
unione([],X,X). unione([X|C],Y,Z) :- appartiene(X,Y), !, unione(C,Y,Z). unione([X|C],Y,[X|Z]) :- unione(C,Y,Z).In questo caso se
X appartiene all'insieme Y
non deve essere incluso nell'unione.
Il cut congela questa scelta.
In caso contrario il cut non viene eseguito e si passa alla
terza clausola che inserisce X nell' unione.
X nel risultato.
% file: cut/insiemi1.pl SBAGLIATO ! % versione sbagliata - e' stato sopresso il "cut % nei predicati intersezione e unione senza altre modifiche appartiene(X,[X|_]) :- !. appartiene(X,[_|Y]) :- appartiene(X,Y). incluso([],_). incluso([X|C],Y) :- appartiene(X,Y), incluso(C,Y). intersezione([],_Y,[]). intersezione([X|R],Y,[X|Z]) :- appartiene(X,Y), intersezione(R,Y,Z). intersezione([_X|R],Y,Z) :- intersezione(R,Y,Z). unione([],X,X). unione([X|C],Y,Z) :- appartiene(X,Y), unione(C,Y,Z). unione([X|C],Y,[X|Z]) :- unione(C,Y,Z). /* provate, forzando il backtraking con "solutions=all", i goal seguenti: ?- intersezione([4],[4,1],L). ?- unione([4],[4,1],L). */Se si prova a passare all' interprete il goal
?- intersezione([4],[4,1],L).L = [4] Yes L = [] Yes No
Nel caso dell'unione se non si inserisce il cut un elemento
X del primo insieme se appartiene al secondo non verrà
inserito nel risultato (seconda clausola). Ma senza il cut
anche la terza clausola può essere utilizzata in queste condizioni,
e l'elemento X comparirà più di una volta.
Ad esempio in risposta alla domanda
?- unione([4],[4,1],L).L = [4, 1] Yes L = [4, 4, 1] Yes No
% file: cut/insiemi2.pl % versione di intersezione e unione che usano "not" % al posto del "cut" appartiene(X,[X|_]) :- !. appartiene(X,[_|Y]) :- appartiene(X,Y). incluso([],_). incluso([X|C],Y) :- appartiene(X,Y), incluso(C,Y). intersezione([],_Y,[]). intersezione([X|R],Y,[X|Z]) :- appartiene(X,Y), intersezione(R,Y,Z). intersezione([X|R],Y,Z) :- \+ appartiene(X,Y), intersezione(R,Y,Z). unione([],X,X). unione([X|C],Y,Z) :- appartiene(X,Y), unione(C,Y,Z). unione([X|C],Y,[X|Z]) :- \+ appartiene(X,Y), unione(C,Y,Z). /* provate, forzando il backtraking con "solutions=all", i goal seguenti: ?- intersezione([4],[4,1],L). ?- unione([4],[4,1],L). */
Utilizzando il not si ha una esecuzione meno efficiente in quanto,
in alcuni casi, il goal appartiene(X,Y) viene invocato
due volte.
0l>
?- trace,percorso(a,b).otteniamo
Call: ( 14) percorso(a, b) ? Call: ( 15) connesso(a, b) ? Call: ( 16) strada(a, b) ? Exit: ( 16) strada(a, b) ? Exit: ( 15) connesso(a, b) ? Exit: ( 14) percorso(a, b) ?Infatti
a e b sono direttamente connessi.
?- trace,percorso(a,e).otteniamo:
Call: ( 28) percorso(a, e) ? Call: ( 29) connesso(a, e) ? Call: ( 30) strada(a, e) ? Fail: ( 30) strada(a, e) ? Redo: ( 29) connesso(a, e) ? Call: ( 30) strada(e, a) ? Fail: ( 30) strada(e, a) ? Redo: ( 29) connesso(a, e) ? Fail: ( 29) connesso(a, e) ? Redo: ( 28) percorso(a, e) ? Call: ( 29) connesso(a, _L302) ? Call: ( 30) strada(a, _L302) ? Exit: ( 30) strada(a, d) ? Exit: ( 29) connesso(a, d) ? Call: ( 29) percorso(d, e) ? Call: ( 30) connesso(d, e) ? Call: ( 31) strada(d, e) ? Fail: ( 31) strada(d, e) ? Redo: ( 30) connesso(d, e) ? Call: ( 31) strada(e, d) ? Exit: ( 31) strada(e, d) ? Exit: ( 30) connesso(d, e) ? Exit: ( 29) percorso(d, e) ? Exit: ( 28) percorso(a, e) ?In questo caso la prima regola per percorso fallisce perchè non c'è una connessione diretta fra
a ed e.
Con il backtracking si passa alla seconda regola, e si cerca una città
direttamente connessa con a.
La regola connesso, rimanda al fatto strada. I fatti vengono scanditi
nell'ordine in cui sono stati immessi nel database del Prolog.
Quindi il primo fatto utlizzato sara strada(a,d)>.
Da d c'è poi una connessione diretta con e.
Il percorso risultante è quindi a-d-e.
percorso(a,c,[a]).[c, b, d, a] Yes [c, e, d, a] Yes [c, b, a] Yes [c, e, d, b, a] Yes NoPoichè
a e a non sono direttamente
connessi si usa per prima cosa la seconda regola per percorso
percorso(X,Y,T) :- connesso(X,Y), write([Y|T]),nl. percorso(X,Y,T) :- connesso(X,Z), Z \= Y, \+ membro(Z,T), percorso(Z,Y,[Z|T]).Si deve quindi trovare uno
Z direttamente connesso ad
a.
La regola connesso rimanda ai fatti strada
che nel nostro esempio compaiono in questo ordine:
strada(a,d). strada(a,b). strada(b,c). strada(c,e). strada(d,b). strada(d,e).Il primo fatto che viene utilizzato è:
strada(a,d).
Si va inizialmente in d e da qui si prosegue.
È quindi l'ordine dei fatti strada che determina
l'ordine con cui vengono trovati i percorsi.
Scambiando i primi due fatti si otterrebbe per primo un percorso
che passa inizialmente per b.
a e c
passando per e si può usare il seguente goal:
?- percorso(a,c,[a],L), member(e,L).e si ottiene:
L = [c, e, d, a] Yes L = [c, e, d, b, a] Yes No