Caratteristiche base del Prolog:

cut, Efficienza,

Negazione e

Significato Logico

dei Programmi,

Predicati Meta-logici ed

Extra-Logici

 

 

 

 

 

 

 

Stefania Costantini

Dipartimento di Informatica, Università degli Studi di L’Aquila

 

 

 

 

 

 

Il predicato predefinito di taglio '!', o 'cut'

 

 

Effetto: ha sempre successo, non viene mai risoddisfatto

 

 

Funzione: inibisce il backtracking

 

 

Come: congelando le scelte effettuate sulla meta genitrice

 

 

Meta genitrice: e' la meta che e' stata risolta mediante Ia                          clausola contenente il !

 


Usi del cut

 

1)  Efficienza: per evitare tentativi inutili di unificazione  (tipicamente: base della ricorsione)

 

2) Per evitare la ricerca di soluzioni alternative
       - inesistenti
       - non volute

 

3) Per evitare computazioni che non terminano, in presenza
       - di infinite soluzioni
       - di potenziali cicli infiniti

 

4)  Per creare strutture di selezione

 

5)  Per far fallire la meta genitrice

 

6) Per realizzare la negazione per fallimento

 


Esempi di uso del cut

 

v   Efficienza + prevenzione ciçlo infinito

      (nella seconda clausola manca un test!!!)

 

fattoriale(O,1):- !

       fattoriale(N,F):- N1 is N1-1, fattoriale(N1 ,F1), . F is N*F1.

 

v   Forzare un' unica soluzione

appartiene (E, [E|C]):-!.

appartiene (E, [_|C]): - appartiene(E,C).

 

In questo caso, altre soluzioni:

- non esistono se la lista ha elementi distinti

- altrimenti, possono esistere ma sono ininfluenti

 

v   Forzare un' unica soluzione

 

ha_figli (X) : - figlio (X, Y) , ! .

 

"X ha figli se esiste Y che e' figlio di X"

 


Esempio

 

a(X):- b(X).                                        [1]

a (X) : - c (X) , ! , d (X) , e (X) .          [2]

a(X):- f(X).                                        [3]

 

?-a(V).

 

o      Si seleziona [1]

o      Se b(X) fallisce si seleziona [2]

o      Se c(X) fallisce si seleziona [3]

o      Se c(X) riesce si esegue!, d (X)

o      Se d(X) riesce si esegue e(X)
 

o      Se d(X) fallisce

- non si ritenta c(X)

- non si seleziona [3]

quindi la meta genitrice a(V) fallisce

o      Se e(X) fallisce

- si ritenta solo d(X), ma non c(X) ne' si seleziona [3]  quindi la meta genitrice a(V) fallisce

 

 

Uso dell' Unificazione nelle definizioni

 

definisco

 

In Prolog: decomp(L1, L2)

decomp ( [], []).

decomp ( [X], X).

decomp([X|Y], Y).

decomp (parola_chiave, ok).

 

?-decomp([] ,L2).

L2 = [].

 

? - decomp (L 1 ,5).

      L1 = [5]

?- decomp([aa, bbb, cc], L2).

L2 = [bbb, cc]

decomp (parola_chiave, L2).

L2 = ok

 

 


Ricorsione ed efficienza

 

Esempio: trovare l’ elemento minimo Min e l'elemento massimo Max di una lista data L.

 

minmax ( ?Min, ?Max, +Lista)

 

*   passo base: lista di un solo elemento

 

*   passo ricorsivo : lista non vuota, minimo e massimo      provvisori

 

*   relazione ausiliaria: min(+X,+Y,?Z) trova il minimo    fra      due elementi

 

*   relazione ausiliaria: max(+X, +Y, ?Z) trova il massimo fra due elementi

 

minmax (X, X, [X]):-!.

minmax(Min, Max, [X|L]):- minmax(MinL, MaxL, L),  

min(X, MinL,Min),

max (X, MaxL, Max).

 

 ­

 


 

 

 

Problema: se la lista è lunga N, le N-1 chiamate a minmax fino a raggiungere il passo base restano in sospeso

 

 

Conseguenza: poiché ad ogni chiamata di procedura e' associato un record di allocazione, si consuma molto spazio.

 

 

Soluzione: procedura ausiliaria con parametri addizionali per propagare fra le successive chiamate i risultati parziali

 

minmax1 (?Min, ?Max, +Lista, ?PMin, PMax)

 

 

 

PMin e PMax sono il minimo e massimo provvisori, ossia quelli trovati fino a quel punto.

 


Ma minmax1 va chiamata con due parametri in più!

 

soluzione: mascherare la presenza di minmax1

 

minmax (X, X, [X]):-! .

minimax(Min, Max, [X|L]) :- minimax1(Min, Max, L, X, X).

Minmax1(PMin, PMax, [], Pmin, PMax):-!.

Miinmax1(Min, Max, [X|L], PMin, PMax):

min (X, Pmin, X), !,

minmax1(Min, Max, L, X, PMax) .

 

minmax1 (Min, Max,  [X|L], PMin, PMax):-

max(X, Pmax, X),

minmax1(Min, Max, L, PMin,X).

 


Definizioni ricorsive: Esercizi sulle liste

 

Append

append ([] ,L,L) :- !.

append([A|X], Y, [A|Z]):- append(X, Y, Z).

 

 

Prefisso

prefisso(P, L):- append (P, _ ,L).

 

oppure

 

prefisso ([] ,L):- !.

prefisso([A|P], [A|C]):- prefisso(P,C).

 

 

Suffisso

suffisso (L,L):- !.

suffisso (S, [_|L]):- suffisso(S, L).

 


 

 

Esercizio da svolgere:

Definire una relazione ternaria scomponi(P, S, L) che è vera se S è una sottolista di L e P è la lista di elementi che precedono S in L

 

Ad esempio vale:

 scomponi ( [c, r, p], [q, g, s], [c, r, p, q, g, s, u, d, f])

 

 

 

Esercizio da svolgere:

Definire una relazione ripulisci che rimuove da una lista data tutte le occorrenze contigue dello stesso elemento, trasformando ad esempio la lista::

[lista, lista, prova, prova, con, elementi, elementi, duplicati]

nella lista

[lista, prova, con, elementi, duplicati]



Negazione in Prolog

 

not(G) o  \+ G

 

il not è un predicato metalogico che rappresenta la negazione per fallimento

 

not(G) riesce se G fallisce come meta Prolog

 

Proceduralmente:

 

not(G):- G, !, fail.

not(_).

 

Importante: il successo di not(G) non può produrre risultati!

 

o      not(G) fallisce se G riesce

o      not(G) riesce se G fallisce:' ma 1Ln1n181 mieta che fallisce non

istanzia variabili

 

Quindi, not(G) in Prolog

e' solo un test sulla riuscita o fallimento di G.

 


Per non creare problemi, not(G) deve essere "ground" (non possono esservi in G variabili non istanziate)

 

Altrimenti: si possono avere risultati anomali

animale (gatto) .

vegetale (melanzana) .

 

La meta

      ?-vegetale (X), not(animale(X) ) .

riesce. Infatti:

-    vegetale (X)       produce il risultato X/melanzana

-    not (animale (melanzana) )    riesce

 

Invece la meta

      ?-not(animale(X) ) , vegetale (X) .

 

fallisce!

Infatti, la prima sottometa

- not(animale(X)) fallisce perché animale(X) riesce con X/gatto

 

Eppure, le due mete sono logicamente equivalenti!

 

Se una meta not(G) non e' completamente istanziata, l'and logico (“,”) non e' piu' commutativo


Osservazione.

 

verde (a) .

verde (b) .

azzurro(c).

?-not (verde(c)).                                      [1]

? -not (verde (d)).                                    [2]

 

*   Le mete [1] e [2] riescono perché i fatti

verde(c).

verde (d) .

 

non sono presenti nel programma

 

 

Ma c’è una differenza concettuale:

 

*   L'oggetto d non e' presente nel programma, c lo è non è possibile distinguere i due casi

 

Nota: l'asserzione "azzurro(c)" non permette di concludere che c non e' verde, perché non c'e' alcuna informazione sul fatto che" azzurro" e "verde" sono colori, e che un oggetto ha un unico colore

 


Fondamenti concettuali della negazione per fallimento

 

Quando una proposizione A (ad esempio "verde (c) ")

Ø    non e presente in un programma

Ø    non è derivabile dal programma.

 

si possono assumere due posizioni radicalmente diverse:

1) non è noto se A sia vera o falsa

2) A viene ritenuta falsa

 

Cosa vuol dire (2):  si assume che tutte e sole le informazioni disponibili siano contenute nel programma.

 

 

Ipotesi del mondo chiuso: regola di inferenza basata su (2)

 

A non è dimostrabile
_________________

A è falsa

 

 

Ma una dimostrazione può non terminare! Allora:

 

Negazione per fallimento finito:

 

 

la dimostrazione di A fallisce finitamente
___________________________________

A è falsa


Relazione fra verità e dimostrabilità

in una teoria assiomatica del primo ordine:

 

v   Se una proposizione e' vera, è dimostrabile

v   Se una proposizione è falsa, vi sono due possibilità:

   - la dimostrazione fallisce in tempo finito

- la dimostrazione non termina

 

Negazione Prolog e negazione in logica classica

 

Logica classica: not(A) e' vero se A e' falso (non vero)

 

Prolog: not(G) e' vera quando G e' falsa, e:

·       G e' completamente istanziata

·       la dimostrazione di G fallisce finitamente

 


Semantica (per grandi linee)

del linguaggio delle clausole di Horn

 

Prolog  =  linguaggio delle clausole di Horn

+

predicati predefiniti

 

La Base di un programma P è composta dalle proposizioni p(A1,An) (p predicato) esprimibile a partire da:

 

- i termini costruiti dai simboli di costante e funzione di P

 

- i predicati di P

 

 

Base del programma


Predicati extra-logici

 

Predicati Predefiniti per l’Aritmetica

 

*    Il predicato predefinito X is Expr istanzia la variabile (libera!) X all’espressione Expr

*    Nell’espressione si possono usare le operazioni aritmetiche e gli operatori di confronto usuali

Unificazione

*   Il predicato predefinito X = Y ha successo se X ed Y unificano, ossia se esse sono variabili libere, o se i termini ai quali sono istanziati unificano. Il predicato produce effettivamente l’unificazione dei due termini. Ad es., se X/f(K) e Y/f(a), X=Y ha successo producendo l’istanziazione K/a.

*   Il predicato X == Y ha successo se X ed Y sono istanziati a termini identici, ed effettua solo un controllo (non produce istanziazioni. Analogamente, X \== Y ha successo se X ed Y non sono istanziate a termini identici

 

Controlli di tipo

*   I predicati predefiniti che controllano il tipo dei termini sono: integer(X), che ha successo se X è istanziato ad un numero intero, atom(X) che ha successo se X è istanziato ad una costante non numerica, constant(X) che ha successo se X è istanziato ad una costante (anche numerica), compound(X) che ha successo se X è istanziato ad un termine con funtore ed argomenti.


Predicati meta-logici

 

*    Modifiche alla base di dati (assert/retract)

 

assert(G) e retract(G) sono predicati metalogici

cioè predicati del linguaggio che agiscono su proposizioni del linguaggio stesso

§       assert(G) aggiunge la clausola G al programma

 

§       retract (G) rimuove la clausola G dal programma

 

Conseguenza:

il Modello del programma cambia in fase di esecuzione

v   Cambia il significato logico del programma

v   La stessa meta, se rieseguita, può dare risultati diversi

 

In quale caso assert(G) non influisce sul significato logico del programma? Quando G e' una conseguenza logica! (asserzione di lemmi)

 

pot(N,0,1):- !.

pot(N, Exp, P):-Exp1 is Exp-1, pot(N, Exp1 ,P1), P is N*P1.

 

?-pot(5, 4, X), assert(pot(5, 4, X)).


Predicati meta-logici

 

*    Ispezione dello stato delle variabili

o      var(Term) ha successo se Term è una variabile non istanziata. Viceversa per nonvar(Term).

*   Questi predicati predefiniti consentono di ispezionare parte dello stato del programma in esecuzione.

*    ?- var(X), X = a. ha successo, perché quando viene chiamato var, X non è istanziata.

*   ?- X = a, var(X. fallisce, perché quando viene chiamato var, X è istanziata.

*   Quindi, la “,” che sta in linea di principio per un AND logico, in presenza di predicati extra-logici può (come in questo caso) non essere più commutativa.

*   Chiamata di un goal: call(G) invoca il goal G. E’ un predicato meta-logico perché il suo argomento non è un termine, ossia non è un oggetto del dominio del programma/teoria logica, ma è un goal, ossia un teorema da dimostrare, che è un oggetto esterno al programma/teoria logica. Ossia, il programma invoca dal suo interno il proprio motore inferenziale su un proprio teorema da dimostrare. Il programma prende così il ruolo dell’utente di se stesso.


Predicati meta-logici

 

*    Ispezione/costruzione di termini e goal: il predicato predefinito univ permette di comporre, scomporre e ispezionare termini e goal, e ha la notazione infissa “=..”.

*   Uso: X =.. [F | L] dove F è un funtore, ed L la lista degli argomenti. F deve essere istanziato, tutti gli altri elementi non necessariamente.

o      X =.. [p, a, b] o equivalentemente X =.. [p, [a, b]] supponendo che X sia istanziata, ha successo se X/p(a,b) oppure se X/p(V,K), producendo V/a,K/b, oppure se X/p(V,b), producendo V/a, oppure se X/p(a,W), producendo W/b.

o      X =.. [p, a, b] o equivalentemente X =.. [p, [a, b]] supponendo che X non sia istanziata, ha successo producendo X/p(a,b).

o      X =.. [p, V, b] o equivalentemente X =.. [p, [a, b]] supponendo che X non sia istanziata, ha successo producendo X/p(V,b).

*   L’uso X=..[F|L] ha senso in particolare quando non si conosce o non si vuole considerare l’arità (numero di argomenti) di F.


Predicati meta-logici

 

*    Da univ si ricavano i predicati ausiliari

o      functor(Term,F,Arity) che estrae funtore e arità di un termine con funtore istanziato: ad es. functor(X,F,Y) con X/p(V,g(H),n) produce F/p, Y/3

o      arg (N,Term,Arg) che estrae l’ennesimo argomento di un termine: ad es. arg(2,X,A) con X/p(V,g(H),n) produce A/g(H)

*   univ si può usare per costruire e chiamare goal, come ad es. con la congiunzione G =.. [q, a, g(W)], call(G).

*   univ si può usare per manipolare goal, come ad es. con la congiunzione G1 =.. [q, L], G2 =.. [p, L], call(G2). dove è stato costruito un nuovo goal con predicato diverso e stessi argomenti.

 

 


Predicati meta-logici

 

 

*      Il meta-predicato clause(H,B) permette di ispezionare  la base di conoscenza del programma. H deve essere un termine con funtore istanziato. Il predicato restituisce tutte le regole del programma la cui conclusione unifica con H, istanziando B con il corpo della regola, sotto forma di congiunzione.

 

 

*      Ad esempio, con le clausole:

p(a) :- q(a).

   p(X):- f(X),q(X,b).

   p(c).

 

la query ?-clause(p(Y),B). restituisce :

 

              Y/a, B/q(a)  e

 

              Y/X, B/(f(X),q(X,b))  e

  

             Y/c, B/true

 

 

Si noti che le congiunzioni sono trattate come le liste, ossia (A,B,C) è equivalente a: ,(A,(B,C)) dove “,” è considerato un funtore. Non esiste però la “congiunzione vuota”.

 

*   I predicati meta-logici fanno sì che il Prolog possa fungere da meta-linguaggio di se stesso, e permettono di costruire meta-interpreti, ossia interpreti Prolog scritti in Prolog.


Esempio. Uso della ricorsione per attraversare strutture dati:

 

Automa a stati finiti ASF

 

 

linguaggio riconosciuto: con n e k dispari

 

Rappresentazione "statica" di un ASF in Prolog

 

stato_iniziale(aa).

stato_finale(cc) .

transizione(aa,a,ab).

transizione(ab,a,aa).

transizione(ab,b,bc).

transizione(bc,c,cc).

transizione(cc,c,bc).

Esempio. Simulazione "dinamica" di un ASF in Prolog

 

Stringa del linguaggio come lista

          es.bc diventa [a,a,a,b,c,c,c,c,c]

 

Il predicato riconosci(+Parola) vale se la parola data e' accettata dall' automa

*   Punto di partenza: stato iniziale

                   

                     riconosci (P):- stato_iniziale (S),  scandisci (P, S).

 

*   Scansione della parola: transizioni fino a:

- termine parola.

- raggiungimento stato finale

 

scandisci ( [] ,S) :- stato_finale (S), !.

scandisci ([E|L] ,S) :-  transizione(S, E, NS),

        scandisci (L,S).




Esempio di uso del backtracking: elaborazioni su grafi

 

Colorazione di un grafo con quattro colori (ad es. rosso, bianco, blu e giallo)

*  a ciascun nodo del grafo e' assegnato un colore  

*  due nodi adiacenti non hanno mai lo stesso colore

 

 

 


Una strategia (ingenua) di soiuzione del problema

 

1.  Preso un nodo non colorato, assegnargli un colore

 

2.  Un nodo adiacente ha lo stesso colore?

             - se no vai a [6]

3.  Tutti i colori sono stati tentati per il nodo?
        - se si vai a [5]

 

4.  Assegna al nodo un nuovo colore (non tentato in precedenza) e torna a [2]

 

5.  Esiste un altro nodo già colorato?

              - se si, vai a [4]

              - se no, vai a [8]

 

6.  Tutti i nodi sono colorati?  

              - se no, vai a [1]

 

7.  Fine con successo

 

8.  Fine con fallimento


 

 

Soluzione in Prolog

 

Ø    predicato colora_grafo (+Nadi, ?Col or azione)

-        Nodi e' la lista dei nodi det gra_o

-         Colorazione restituisce le coppie nodo-colore sotto forma di una lista di termini c(N,C) dove N e' istanziato ad un vertice e C ad un colore

Ø    predicato

colora (+Nodi, +Tentativo_Colorazione, ?Colorazione)

e' un predicato ausiliario, con l'argomento addizionale Tentativo_Colorazione  (inizialmente []) che contiene la colorazione provvisoria

 

Ø    predicato

              colora_nodo(+Nodo, -Colore, +Tentativo_Colorazione)

              restituisce un colore Colore da assegnare al nodo Nodo

 

Ø    predicato
colore_ok (+Nodo, +Colore, + Tentativo_Colorazion                 vero se nessun nodo adiacente a Nodo ha colore Colore

 


Descrizione della situazione

 

arco (1 ,2) .

arco (1 ,3) .

arco (1 ,4).

arco (2,3).

arco (2,4).

arco (2,5).

arco (3,4).

arco (4,5).

 

colore (bianco) .

colore (rosso) .

colore (blu) .

colore (giallo) .

 


Definizioni

 

colora_grafo(G, L):-colora(G, [], L).

 

/* * * * * * * *---- * * * * * * * *---- * * * * * * * * /

 

colora([] , L, L) :- ! .

colora([N | R], P, L):- colora_nodo (N,C,P) ,

colora(R, [c(N,C) |  P], L).

 

/* * * * * * * *---- * * * * * * * *---- * * * * * * * * /

 

colora_nodo (N, C, P):- colore (C) ,

                                     colore_ok(N, C, P).

 

/* * * * * * * *---- * * * * * * * *---- * * * * * * * * /

 

colore_ok(N, C, P):-   (arco(N, N1) ; arco(N1 ,N)),

                                                       appartiene (c (N1,C), P),

                                                       ! , fail.

Colore_ok(_ , _ , _).

 

/* * * * * * * *---- * * * * * * * *---- * * * * * * * * /

 

appartiene (E, [E | X]):- ! .

appartiene (E, [_ | X]):- appartiene(E, X).

Problemi con infinite soluzioni

 

arco(1 ,2).

arco (2,3).

arco (3,4).

arco (4) 1).

 

 

 

Predicato

 

cammino (+Nodo 1, +Nodo2, ?Cammino).

cammino(N1 ,N2, [N1 ,N2]):- arco(N1 ,N2).

cammino(N1 ,N2, [N1IC]) :-arco(N1 ,N) ,cammino(N,N2,C).

 

Esistono infiniti cammini fra due nodi!

 

 

 

 

 

 

Adiacenti:

 

?- cammino (4, 1, C).

C=[4, 1];

C=[4, 1, 2, 3, 4, 1];

C=[4, 1, 2, 3, 4, 1, 2, 3, 4, 1]

 

 

 

 

 

Non Adiacenti:

 

?- cammino(3,1 ,C).

C=[3, 4, 1] ;

C= [3, 4, 1, 2, 3, 4, 1] ;

C=[3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1]

 


 

Rimedio per evitare le infinite soluzioni

 

cammino(N1 ,N2, [N1, N2]):- arco(N1 ,N2), ! .

cammino(N1 ,N2, [N1 | C):-arco(N1 ,N),

                                             cammino (N,N2,C) , ! .

 

·       il primo ! evita la ricerca di altri cammini se i nodi sono

adiacenti

             

              ?- cammino(4, 1, C).

                     C= [4,1];

no

 

·   il secondo ! evita la ricerca di più di un cammino se i nodi non sono adiacenti

 

               ?- cammino(3, 1 ,C).

                      C=[3,4,1] ;

                      no

 



Strutture di selezione

 

Dati due segmenti [X1,X2] e [Y1,Y2] decidere se hanno intersezione non vuota

 

              X1 |---------------------------| Y1

                                  X2 |---------------------------| Y2

 

 

              X1 |----------------------------------------------------| Y1

                                  X2|----------------------| Y2

 

inters([X1, Y1]  ,[X2,Y2]):- X1=<X2, ! , (X2=<Y1 ; Y2=<Y1).

 

                                           X1 |---------------------------| Y1

                     X2|--------------------------|Y2

 

                           X1|---------------------------------|Y1

       X2|-------------------------------------------------------|Y2

 

 

inters([X1 ,Y1] , [X2, Y2]):- X2=<X1, (X1 =<Y2; Y1=<Y2).