Prolog - Informatica

Transcript

Prolog - Informatica
Prolog
Intelligenza artificiale
Stefano De Luca
Temi







Basics
Liste
Ricursione (sulle liste)‫‏‬
Grafi
Alberi
Database
Predicati di secondordine
Basics

Cfr. slides di Marco Pirrone
Liste

Le liste sono oggetti nativi rappresentate con la
sintassi
[a, b, c, …, d ]



dove a, b… sono gli elementi della lista
Ogni lista termina con un elemento “nascosto” che è
la lista vuota, che si rappresenta con []
È possibile dividere la lista in due parti, il suo primo
elemento (la testa) e la parte restante (la coda)
tramite la sintassi [H|T] . Ad es.
[a, b, c] = [H|T] unifica con H=a e T=[b,c]
Importante notare che la testa non è una lista,
mentre la coda lo è
Ricursione sulle liste



Sfruttando la sintassi che abbiamo visto, possiamo operare
facilmente sulle liste con la ricursione
Sarà necessario definire la condizione di stop (tipicamente
associata al caso zero, ovvero la lista vuota), e quindi
definire il caso generale
Ad esempio, se vogliamo generare una lista che contenga il
doppio dei valori di un’altra (ovvero: doppio([1,2,3],
[2,4,6]) potremo definire un predicato così:
doppiol([], []).
% un fatto.
% il doppio di una lista vuota è la lista vuota
doppiol([H|T], [H1|T1]) :H1 is H * 2,
% qui si calcola il doppio della testa
doppiol(T, T1).
% avendo definito la testa,
% la coda viene calcolata per ricursione
Grafi


Un grafo può essere
rappresentato tramite
l’insieme dei predicati che
descrivono gli archi e i vertici
edge(1, 5). edge(1, 7).
edge(2, 1). edge(2, 7).
edge(3, 1). edge(3, 6).
edge(4, 3). edge(4, 5).
edge(5, 8). edge(6, 4).
edge(6, 5). edge(7, 5).
edge(8, 6). edge(8, 7).
vertices([1, 2, 3, 4, 5, 6, 7,
8]).
Esempio di uso dei grafi


Obiettivo: definire un predicato che
vada da un nodo all’altro evitando i
loop,
Il predicato indicherà la partenza,
l’arrivo, i nodi visitati e il path
percorso:
path(Start, Finish, Visited, Path).

Definiamolo per ricursione:

Quando abbiamo raggiunto il nodo finale,
abbiamo completato la ricerca (il caso zero):
path(Node, Node, _, [Node]).

Il caso generale: un path da Start a Finish
inizia con un nodo X connesso a Start, seguito
da un path da X a Finish:
path(Start, Finish, Visited, [Start | Path]) :edge(Start, X),
not(member(X, Visited)),
path(X, Finish, [X | Visited], Path).
Rappresentazione degli stati

Vogliamo risolvere il problemi dei cannibali
e dei missionari:



tre cannibali e tre missionari sono su una
sponda di un fiume dove c’è una barca con soli
due posti a disposizione
Devono attraversare il fiume a coppie, ma i
missionari non vogliono rimanere in minoranza,
per paura di essere mangiati
Si rappresenta lo stato finale (il goal) come
uno stato che indichi cosa vogliamo
ottenere
Gli stati


Lo stato indica la situazione ad un
momento specifico della soluzione (una
situazione)‫‏‬
Nell’esempio, basta indicare:




Il numero di missionari sulla sponda sinistra
Il numero di cannibali sulla sponda sinistra
Su quale sponda è la barca
Cosa che possiamo ottenere con il
predicato:

state(Missionaries, Cannibals, Side)
La soluzione

La soluzione consiste di una lista di mosse:
[move(1, 1, right), move(2, 0, left)]


Che indica che un missionario e un
cannibile vanno sulla sponda destra e
quindi due missionari sulla sinistra
Per evitare i cicli, dobbiamo verificare di
non aver già raggiunto uno stato verificato
usando una lista degli stati percorsi, dalla
forma:
[MostRecent_State | ListOfPreviousStates]
Usare i grafi per risolvere problemi

Per risolvere un problema, possiamo usare
lo stesso approccio seguito per ricercare su
un grafo:





Si inizia da uno stato iniziale
Si trovano (o si generano) i nodi espansi, ovvero
gli stati “vicini”
Si controlla che i nuovi stati non siano già stati
visitati
Si trova un percorso dallo stato vicino al goal
Nell’esempio, la ricerca termina quando lo
stato è:
state(0, 0, right).
Main code





% mandc(CurrentState, Visited, Path)‫‏‬
mandc(state(0, 0, right), _, []).
mandc(CurrentState, Visited, [Move | RestOfMoves]) :newstate(CurrentState, NextState),
not(member(NextState, Visited)),
make_move(CurrentState, NextState, Move),
mandc(NextState, [NextState | Visited],
RestOfMoves]).
make_move(
state(M1, C1, left),
state(M2, C2, right),
move(M, C, right)) :M is M1 - M2, C is C1 - C2.
make_move(
state(M1, C1, right),
state(M2, C2, left),
move(M, C, left)) :M is M2 - M1, C is C2 - C1.
Mosse possibili

Bisogna sapere quali sono le mosse
possibili, quindi definiamo i predicati:
carry(2,
carry(1,
carry(1,
carry(0,
0).
0).
1).
1).
carry(0, 2).

Dove carry(M, C) indica che la barca
porta M missionari e C cannibali
Mosse possibili



Una volta trovata una mossa potenziale, bisogna
verificare che sia possibile
Nell’esempio, non è possibile muovere più missionari
o più cannibali di quelli presenti su una sponda.
Quando lo stato è
state(M1, C1, left)
e proviamo carry(M, C) deve essere vero che
M <= M1 and C <= C1

Quando lo stato è
state(M1, C1, right)
e proviamo carry(M, C) allora si deve verificare che
M + M1 <= 3 and C + C1 <= 3
sia vero
Mosse legali


Una volta verificato che la mossa è
possibile, bisogna verificare che sia
legale
Nell’esempio, non si deve lasciare un
missionario da solo:
legal(X, X).
legal(3, X).
legal(0, X).
Nuovi stati

Da ultimo, si generano i nuovi stati:
newstate(state(M1, C1, left), state(M2, C2, right)) :-
carry(M, C),
M <= M1,
C <= C1,
M2 is M1 - M,
C2 is C1 - C,
legal(M2, C2).
newstate(state(M1, C1, right), state(M2, C2, left)) :carry(M, C),
M2 is M1 + M,
C2 is C1 + C,
M2 <= 3,
C2 <= 3,
legal(M2, C2).
Ricerche (attraversamenti)‫‏‬



Depth first
Breadth first
Altri sistemi (iterative deepening, best-first,
A* etc.)‫‏‬
Ricerca depth-first

La ricerca in un grafo depth-first è
un semplice caso di ricursione,
dando il caso iniziale e il caso finale
(stop)‫‏‬
dfs(S,[S]) :- stop(S).
dfs(S,[S|Rest]) :arc(S,S2),
dfs(S2,Rest).
Ricerca breadth-first
bfs :- start(S), bfs(S, Path), showPath(Path).
bfs(S,Path) :-

La breadth
first richiede
l’uso di una
coda (l’elenco
dei nodi
attraversati
all’ultimo
stadio)‫‏‬
empty_queue(Q1),
head_queue([S],Q1,Q2),
bfs1(Q2,Path).
bfs1(Q,[G,S|Tail]) :head_queue([S|Tail],_,Q),
arc(S,G),
stop(G).
bfs1(Q1,Solution) :queue_head([S|Tail],Q2,Q1),
findall(
[Succ,S|Tail],
(arc(S,Succ), \+member(Succ,Tail)),
NewPaths
),
queue_last_list(NewPaths,Q2,Q3),
bfs1(Q3,Solution).
Database


Finora abbiamo visto i fatti soltanto inseriti da
programma, ovvero in modo statico
È possibile intervenire sul database delle informazioni
(la knowledge base) usando alcuni predicati:



I termini possono essere rimossi con i predicati



assert(Termine) e le varianti
asserta(Termine) ed assertz(termine)‫‏‬
retract(Termine)‫‏‬
retractall(Head)‫‏‬
Si possono indicizzare i dati con


recorda(+Chiave, +Termine)‫‏‬
recorded(+Chiave, -Termine)‫‏‬



Nel caso si vogliano inserire i dati in parte
da programma e in parte a runtime, va
segnalato che il predicato è dinamico:
:- dynamic arc/2.
% si indica il predicato e la sua arità
% ovvero il numero di argomenti
arc(1, 3).
arc(3, goal).
Quindi si possono fare delle assert:
assert(arc(1,2)).
Elenco di risultati

Ci sono dei metapredicati che consentono di ottenere tutte
le soluzioni di una query:




Es: Nell’esempio del grafo precedente, se volessimo
conoscere tutti i nodi che sono collegati al nodo 5,
scriveremo:




setof(+Template, +Goal, -Set)
bagof(+Template, +Goal, -Bag)
findall(+Template, +Goal, -Bag)
findall(X, edge(X, 5), L).
L = [1, 4, 6, 7] ;  risultato
Cioè, la prima variabile è quella di cui vogliamo avere tutti i
risultati, il secondo elemento è il termine che vogliamo
unificare, il terzo è la lista risultante
Con bagof è possibile avere altre variabili libere nel termine.
Con il costrutto +Var^Goal diciamo a bagof di non fare il bind
della variabile Var
setof è simile a bagof, ma senza duplicati
Esempio di bagof e setof




2 ?- listing(foo).
foo(a, b, c).
foo(a, b, d).
foo(b, c, e).
foo(b, c, f).
foo(c, c, g).
Yes
3 ?- bagof(C, foo(A, B, C), Cs).
A = a, B = b, C = G308, Cs = [c, d] ;
A = b, B = c, C = G308, Cs = [e, f] ;
A = c, B = c, C = G308, Cs = [g] ;
No
4 ?- bagof(C, A^foo(A, B, C), Cs).
A = G324, B = b, C = G326, Cs = [c, d] ;
A = G324, B = c, C = G326, Cs = [e, f, g] ;
No