Il 100% ha trovato utile questo documento (1 voto)
291 visualizzazioni

Prolog It

Questo documento introduce la sintassi del linguaggio Prolog. Descrive i termini, le costanti, le variabili e le strutture dati usate in Prolog come liste e alberi. Spiega inoltre concetti come predicati, programmi e quesiti.

Caricato da

Dima Dimka
Copyright
© © All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato DOCX, PDF, TXT o leggi online su Scribd
Il 100% ha trovato utile questo documento (1 voto)
291 visualizzazioni

Prolog It

Questo documento introduce la sintassi del linguaggio Prolog. Descrive i termini, le costanti, le variabili e le strutture dati usate in Prolog come liste e alberi. Spiega inoltre concetti come predicati, programmi e quesiti.

Caricato da

Dima Dimka
Copyright
© © All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato DOCX, PDF, TXT o leggi online su Scribd
Sei sulla pagina 1/ 31

Contenuto

Introduzione al Prolog............................................................................................................. 3
1. La sintassi del Prolog ........................................................................................................... 5
1.1 Termini..................................................................................................................................................5
1.2 Proposizioni, Programmi Prolog, Quesiti .............................................................................................8
2. Operatori .......................................................................................................................... 14
2.1 Gli operatori in Prolog. .......................................................................................................................14
2.2 Precedenza di un operatore. ..............................................................................................................14
2.3 Associatività di un operatore..............................................................................................................15
2.4 Dichiarazione ed uso di operatori. .....................................................................................................17
2.5 Confronto di espressioni aritmetiche. ................................................................................................19
2.6 Operatori logici ...................................................................................................................................20
3. Loops ................................................................................................................................ 22
3.1 Loop di un numero fisso di volte ........................................................................................................22
3.2 Fare il ciclo fino a quando una condizione è soddisfatta ...................................................................22
3.3 Backtracking con fallimento ...............................................................................................................23
4. Liste .................................................................................................................................. 24
4.1 Notazione ...........................................................................................................................................24
4.2 Testa e corpo dell'elenco ....................................................................................................................24
5. Predicati di controllo e di ingresso/uscita .......................................................................... 27
5.1 Predicati predefiniti di controllo. .......................................................................................................27
Bibliografia ........................................................................................................................... 31

2
Introduzione al Prolog
Obiettivo di questa parte del corso è di costituire un'introduzione all'uso della logica simbolica
come linguaggio di programmazione. Questa possibilità si è affermata con il nome di
programmazione logica ed ha trovato una concreta realizzazione nel linguaggio Prolog.
La programmazione logica differisce significativamente dalla programmazione tradizionale (con
linguaggi anche ad alto livello quali Fortran, Cobol, Basic, Algol, Pascal, Ada, . . .) in quanto
richiede - e consente - al programmatore di descrivere la struttura logica del problema piuttosto che
il modo di risolverlo: è compito del programmatore far sì che il programma logico rappresenti
adeguatamente il problema. È invece compito del sistema Prolog utilizzare le informazioni presenti
nel programma per effettuare le operazioni necessarie a dare risposte al problema, sfruttando un
potente meccanismo di deduzione logica insito in esso.
Da un punto di vista concettuale, il programmatore può così concentrarsi sugli aspetti logici del
problema e sul modo migliore per rappresentarli, senza essere distratto dalla necessità di fornire
dettagli sul modo di pervenire ai risultati. Da un punto di vista operativo, i programmi Prolog
richiedono un minore tempo di sviluppo, risultano più brevi e compatti in termini di numero di
istruzioni, e più generali come risultati ottenibili dall'esecuzione del programma, rispetto ai
programmi scritti in linguaggi tradizionali.
A partire dall'esplicitazione di tali caratteristiche, nuove e rilevanti per l'attività di programmazione
e per le applicazioni, questa parte del corso si propone di sviluppare una trattazione argomentata,
ma non apologetica, di questo approccio e di questo linguaggio. Lo scopo è di garantire allo
studente tanto uno strumento effettivo di lavoro quanto la comprensione dei concetti che ne stanno
alla base, tentando di conciliare le due esigenze - entrambe imprescindibili - di chiarezza e di rigore.
Le caratteristiche principali di Prolog
a) È un linguaggio di programmazione logica centrato sui dati
Si differenzia da Pascal, C, che sono linguaggi imperativi guidati dal processo. Il paradigma
fondamentale della programmazione logica è la programmazione dichiarativa. L'obiettivo della
programmazione logica è identificare la nozione di computazione e la nozione di deduzione. Più
in particolare, i sistemi di programmazione logica riducono l'esecuzione di un programma al rifiuto
delle clausole del programma, insieme alla negazione della proposizione che esprime la domanda.
Ecco la regola: un rifiuto è una deduzione dal contrario. Si basa sulla deduzione lineare usando le
clausole di Horn. È usato per esprimere relazioni tra oggetti e ottenere informazioni sugli oggetti e
le relazioni tra loro.

3
b) È un linguaggio descrittivo
Scrivere un programma per computer significa 2 cose:
- specifica precisa dei risultati desiderati;
- Progettare il metodo con cui vengono ottenuti questi risultati.
Una lingua è descrittiva se l'attenzione si concentra sul primo aspetto, la scelta del metodo è lasciata
al computer. Un linguaggio procedurale è quello in cui deve essere progettato il metodo per ottenere
i risultati.
c) è usato in:
- Applicazioni di programmazione nell'intelligenza artificiale;
- sviluppo di sistemi esperti;
- elaborazione del linguaggio naturale (compresa la traduzione automatica);
- robotica;
- teoria del gioco;
- processi logici.

4
1. La sintassi del Prolog
La sintassi di un linguaggio descrive come devono essere combinate insieme le parole affinché
abbiano un significato corretto.
Esempio: in italiano, la sintassi della frase “io vedo una zebra” è corretta, ma la sintassi “zebra
vedo io una” non è corretta.
I programmi in Prolog sono costituiti da termini.
Ogni termine è scritto come una sequenza di caratteri.
Il Prolog riconosce due tipi di caratteri
 caratteri stampabili
lettere maiuscole (ABCDE……XYZ)
lettere minuscole (abcde……….xyz)
cifre (0123456789)
simboli (+ - * / \ ^ < > = ‘ “ : . ? @ # $ &)
 caratteri non stampabili
spazio bianco
return

1.1 Termini
In generale, i nomi degli oggetti sono detti termini. I termini non contenenti variabili sono detti
termini chiusi (ground terms). Riassumendo, sono quindi termini:

1.le costanti;
2.le variabili;
3. le strutture, cioè le espressioni della forma f(t1, t2, ..., tn), dove f è un funtore n-ario e t1, t2, ...,
tn sono termini.

Si noti che la definizione dei termini è ricorsiva, ossia ciò che si sta definendo (un termine) compare
(ricorre) nella definizione stessa. Una definizione ricorsiva consente di costruire termini
arbitrariamente complessi. Le definizioni ricorsive rivestono un ruolo molto importante in Prolog,
e saranno considerate più ampiamente nel seguito.

5
Prolog mette a disposizione il termine, utilizzabile ricorsivamente, come unico strumento di
rappresentazione di un oggetto, di applicabilità generale. Il tipo di un oggetto, in base alle
convenzioni di scrittura viste prima, è rappresentato da
lla forma sintattica del termine che lo denota, ed è quindi riconosciuto senza necessità di
specificarlo esplicitamente: non occorrono, in altre parole, dichiarazioni di tipo. L'insieme degli
oggetti denotati da tutti i termini usati in una data rappresent
azione è detto l'universo del discorso, ossia costituisce tutto ciò di cui si parla in quella
rappresentazione.
1. Le costanti

Le costanti sono pensate per dare il nome ad un oggetto specifico o a una relazione specifica.
Ci sono due tipi di costanti: gli atomi e gli interi.
Esempi di atomi sono:
piace antonio tiziano francesco pasta vino strudel pizza gelato sorella genitori

Tipi di costanti
Costanti individuali
denotano specifici elementi del dominio
Esempi:
marco, sabrina, 1, 2900, italia,francia, françois, danielle.
Costanti funzionali
denotano funzioni sugli individui (o n-uple di individui) del dominio
Esempi:
il_padre_di, la_madre_di, età_di,il_presidente_di, +, ∗.
Costanti relazionali (simboli di predicato)
designano proprietà degli individui dell’universo del discorso o relazioni tra di essi.
Esempi: cittadino, persona_fisica, nato_in, lavora_per.

Gli atomi
Ci sono due tipi di atomi:
1. quelli costituiti da lettere e cifre: devono cominciare con la lettera minuscola;
2. quelli costituiti da simboli: normalmente sono costituiti solamente da simboli.
Se è necessario che un atomo cominci con una lettera maiuscola, con una cifra o contiene dei
simboli deve essere racchiuso traapici (‘).
‘Maria’ ‘[email protected]’ ‘33trentini’
I simboli speciali che il Prolog usa per indicare domande (?-) e regole (:-) sono anch’essi degli
atomi

Gli interi

Gli interi sono usati per rappresentare i numeri,


sono costituiti da cifre e non possono contenere
decimali.
0 1 999 512 8192 14765 6224

6
2. Le Variabili

È possibile dare un nome, oltre che ad oggetti particolari, anche ad oggetti specifici ma da
determinare, cioè non ancora identificati (in modo analogo all'uso del pronome nel linguaggio
naturale). Oggetti non specificati sono rappresentati da nomi di variabili; anche questi ultimi sono
arbitrari, ed in Prolog sono caratterizzati dall'iniziare con una lettera maiuscola, o con "_". Si noti
che, mentre costanti distinte denotano sempre oggetti diversi, questo non vale per le variabili, in
quanto variabili distinte potranno venire a rappresentare lo stesso oggetto.
I nomi di variabili possono stare al posto dei nomi di costanti, specificamente come argomenti di
funtori. Ad esempio:

punto(3, 7, 25)

rappresenta uno specifico punto nello spazio, mentre:

punto(3, 7, Z)
punto(3, Y, Z)
punto(X, Y, Z)

rappresentano punti da determinare.


Si possono usare le variabili per denotare non solo un singolo oggetto da determinare, ma anche
una classe di oggetti. Ad esempio:

quadro(tintoretto, Titolo)

"tutti i quadri di Tintoretto"

quadro(tintoretto, olio(Titolo, 1572))

"tutti i quadri ad olio dipinti da Tintoretto nel 1572".


Descrizione di un oggetto singolo ma non ancora definito e descrizione di una classe di oggetti
sono due modi diversi (complementari) di intendere una stessa rappresentazione: un singolo
oggetto la cui tipologia è nota solo come schema generale può essere considerato come
rappresentativo della classe di tutti gli oggetti di quella tipologia.
A volte non si è interessati a certi aspetti di un oggetto e non si ha quindi bisogno di nomi di
variabili per riferirsi ad essi. Questi aspetti possono essere sostituiti da variabili anonime, scritte
con "_"; ad esempio:

quadro(tintoretto, olio(Titolo, _))

"tutti i quadri ad olio dipinti da Tintoretto (non importa quando)".

Un altro uso ancora è quello di una variabile in comune tra più oggetti per rappresentare un
vincolo tra essi; ad esempio:

periodo(data(X1, Y1, Z), data(X2, Y2, Z))

7
indicando che la terza componente, qualunque essa sia, è la stessa per entrambe le date, impone il
vincolo che il periodo sia non superiore ad un anno. In questo caso si dice che la variabile è
condivisa(shared).

3. Le strutture
Il terzo tipo di termine adoperabile in Prolog è la struttura. Una struttura è un singolo oggetto che
consiste di un insieme di altri oggetti, chiamati componenti. Le strutture aiutano ad organizzare i
dati in un programma, perché permettono di trattare un gruppo di informazioni correlate come un
singolo oggetto, senza ricorrere ad entità separate. Le strutture sono utili quando all’interno
programma c’è un tipo di oggetto di cui esistono molte istanze. Una struttura si scrive specificando
il su funtore e i suoi componenti. Il funtore è scritto prima della parentesi tonda, i componenti sono
racchiusi fra parentesi tonde e separati da virgole.

possiede(antonio,libro(malavoglia,autore(giovanni,verga))).

“libro” è il funtore, “malavoglia” e “autore” sono i componenti. A sua volta “autore” è una struttura
innestata.
Le strutture possono essere presenti nelle domande. Per esempio, possiamo domandare se Antonio
possiede un libro di Verga.

?- possiede(antonio,libro(X,autore(giovanni,verga))).

1.2 Proposizioni, Programmi Prolog, Quesiti

Proposizioni

Se i termini denotano oggetti e i predicati denotano relazioni tra gli oggetti denotati dai termini, le
proprietà logiche delle relazioni sono descritte da proposizioni (sentences).

Nella notazione Prolog, ogni proposizione termina con un punto. I predicati stessi, singolarmente
considerati, possono costituire proposizioni atomiche. Ad esempio:
ama(mario, luisa).
è una proposizione atomica (di cui è ovvio il significato associato). Proposizioni non atomiche sono
costituite da più predicati connessi da operatori (o connettivi) logici, denotati da simboli speciali.

8
Il connettivo ":-" è detto condizionale ("se"), o implicazione("è implicato da"). Ad esempio, la
proposizione:
ama(mario, luisa) :- ama(luisa, mario).
è costituita dai predicati ama(mario, luisa) e ama(luisa, mario) connessi dall'implicazione, e si
può leggere come: "mario ama luisa se luisa ama mario", oppure "mario ama luisa è implicato da
luisa ama mario", o ancora, equivalentemente, "luisa ama mario implica mario ama luisa".
Il connettivo "," denota una congiunzione ("e"). Ad esempio, la proposizione:
ama(mario, luisa) :ama(luisa, mario), ama(luisa, poesia).
è costituita dai predicati ama(luisa, mario) e ama(luisa, poesia), connessi tra loro dalla
congiunzione e connessi al predicato ama(mario, luisa) dall'implicazione; si può leggere come:
"mario ama luisa se luisa ama mario e luisa ama la poesia". I predicati a destra del simbolo
condizionale sono detti premesse o condizioni, e il predicato a sinistra è detto conclusione.
Proposizioni prive di variabili sono dette proposizioni chiuse (ground sentences).
In Prolog le proposizioni atomiche base sono anche dette fatti. Le proposizioni possono essere
costituite da predicati contenenti variabili. In questo caso esse sono intese come affermazioni che
riguardano tutti i possibili oggetti rappresentati dalle variabili. In italiano, questo corrisponde
all'uso dei pronomi, come "tutti", "ogni", "chiunque", "qualunque". Ad esempio, le proposizioni:
ama(mario, X).
ama(mario, X) :- ama(X, poesia).ama(mario, X) :- donna(X), ama(X, poesia).
si possono leggere rispettivamente come: "mario ama tutti" o "mario ama chiunque", "mario ama
chiunque ami la poesia" e "mario ama qualunque donna ami la poesia".
Si osservi che un dato nome di variabile rappresenta sempre lo stesso oggetto all'interno di una
proposizione, mentre nomi di variabili uguali in proposizioni diverse non hanno relazione tra loro,
cioè non rappresentano lo stesso oggetto. Si dice che la proposizione è il campo di validità lessicale
(Iexical scoping fieldo semplicemente scope) di un nome di variabile.
Le proposizioni atomiche sono dette anche asserzioni. Le proposizioni non atomiche condizionali
(contenenti cioè l'implicazione) in Prolog vengono dette anche regole. Una proposizione costituisce
un'affermazione su oggetti specifici, se è una proposizione di base, o su classi di oggetti, se è una
proposizione con variabili.
Una singola regola può sintetizzare un insieme di fatti; per esempio, i fatti:
pari(2).
pari(4).
...
possono essere espressi sinteticamente dalla regola:

9
pari(X) :-divisibile(X, 2).
Si può vedere questa regola come la definizione della proprietà pari mediante la relazione
divisibile.
In generale, usando l'implicazione, si può definire una relazione mediante una o più altre relazioni;
ad esempio:
figlio(X, Y) :- maschio(X), padre(Y, X).
Si può definire una relazione inversa di un'altra relazione; ad esempio:
figlio_a(X, Y) :- padre(Y, X).
È possibile definire una gerarchia di proprietà, come in:
animale(X) :- cane(X).
che esprime che "tutti i cani sono animali" ovvero "i cani sono un sottoinsieme degli animali".
S'intende che, con questo significato associato, non è possibile la definizione inversa:
cane(X) :- animale(X).
La gerarchia può essere multipla; ad esempio:
animale(X) :-carnivoro(X).
carnivoro(X) :-cane(X).
In generale, una relazione può essere definita mediante altre relazioni, queste a loro volta con altre,
e così via. Si ha quindi una gerarchia di relazioni; ad esempio:
cugini(X, Y) :-genitore(S, X), genitore(T, Y), fratelli(S, T), X\= Y.
fratelli(X, Y) :-genitore(Z, X), genitore(Z, Y), X\= Y.
Si noti che il fatto che un individuo non è cugino o fratello di se stesso, ovvio nel significato usuale
di questi termini, deve essere espresso esplicitamente nella definizione formale, mediante il
predicato "\=" ("diverso da"). Quest'ultimo è un predicato predefinito, ovvero il suo nome, la sua
molteplicità ed il significato associato sono ritenuti già noti in Prolog e quindi possono essere usati
senza definirli esplicitamente. Vi sono in Prolog numerosi predicati predefiniti, che saranno
considerati più avanti.Naturalmente la relazione cugini può essere espressa in un'unica
proposizione, come segue:
cugini(X, Y) :- genitore(S, X), genitore(T, Y), X \= Y, genitore(Z, S), genitore(Z, T), S \= T.
Quest'ultima rappresentazione è più compatta della precedente, ma la prima, oltre a suddividere la
definizione in due sottoparti singolarmente più semplici, introduce la relazione fratelli che in questo
contesto fa da intermediaria tra le due sottoparti della definizione, ma può essere utile anche in altri
contesti, come in effetti avviene nell'uso di queste relazioni nella lingua naturale. In ogni caso, vale
sempre la considerazione che, fra i vari modi possibili di formulare le regole, si sceglie quello
considerato più pertinente ed utile allo scopo della rappresentazi.

10
Programmi Prolog
Una proposizione del tipo:
A:-B1, B2, ..., Bn.
dove A, B1, B2, ..., Bn sono predicati, è detta una clausola di Horn(nel seguito chiamata
semplicemente clausola). La conclusione A, che è costituita da un unico predicato, è detta testa
della clausola. La congiunzione delle condizioni B1, B2, ..., Bn è detta corpo, o coda, della clausola.
Le condizioni possono mancare, cioè si può avere la conclusione senza condizioni (e si omette
anche il simbolo ":-"); in questo caso si parla di clausola unitaria.
Si può ossrvare che , strutturalmente, anche le clausole possono essere viste come termini. Infatti,
una clausola unitaria ha già la forma di un termine (si è detto precedentemente dell'uguaglianza
formale tra predicati e funtori). Una clausola non unitaria con n > 0 condizioni può essere vista
come un termine che ha come funtore n+1-ario il connettivo ":-", come primo argomento la testa
della clausola, e come argomenti successivi le n condizioni del corpo della clausola, come segue:
: - (A, B1, B2, ..., Bn).
Questa considerazione evidenzia l'uniformità del linguaggio, ed è alla base della possibilità di
trattare clausole, e quindi più in generale programmi, come dati.
Un programma Prolog è un insieme finito di clausole, unitarie e non, scritte in sequenza. Tale
insieme di clausole è detto anche la base di dati del programma. Essa rappresenta l'insieme delle
conoscenze espresse (come fatti e come regole) sul problema.
Riassumendo, il linguaggio con cui descrivere un problema è costituito da:
1. i vocabolari (insiemi di simboli) di costanti, variabili, funzioni e predicati; questi simboli,
ed il significato ad essi associato, sono scelti dall'utente del linguaggio, in funzione del
problema considerato;
2. i connettivi logici ed i simboli ausiliari (le parentesi), il cui significato è prestabilito in
Prolog;
1. le proposizioni costruite con i simboli precedenti, secondo le regole Prolog considerate.
Questo linguaggio consente di rappresentare problemi che concernono oggetti, relazioni tra oggetti,
e proprietà logiche delle relazioni. La semplicità della sintassi del Prolog deriva dalla semplicità
della sintassi del linguaggio logico. Il linguaggio della logica simbolica è allo stesso tempo
semplice, non ambiguo e di grande potenza espressiva, e può essere considerato come una forma
semplificata della lingua naturale.
Quesiti
Si è detto prima che, se si vuole dimostrare che una proposizione è vera rispetto a un dato insieme
di proposizioni, la si nega, scrivendola nella forma:
:-p1, ... pn.

11
e si cerca di derivare una contraddizione. Nell'ambito della programmazione logica e della
dimostrazione per confutazione, tale proposizione negativa è chiamata un quesito (query), la cui
risposta è positiva se si deriva la clausola vuota, negativa altrimenti. Nella esplicazione
dichiarativa, la proposizione negativa può essere espressa in forma interrogativa; per evidenziare
questo, nel seguito si userà nei quesiti il simbolo "?-" al posto di ":-". Consideriamo il classico
esempio di sillogismo aristotelico, espresso nella forma a clausole:
uomo(socrate). /* socrate è un uomo */
mortale(X) :- uomo(X). /* tutti gli uomini sono mortali */
e poniamo ilquesito:
?- mortale(socrate). /* socrate è mortale? */
Mediante la sostituzione X/socrate si ha la risoluzione tra il quesito e la regola, ottenendo la
risolvente.
:- uomo(socrate).
e quindi la risoluzione tra questa ed il fatto, da cui si deriva:
:-
concludendo così che la risposta è positiva ("socrate è mortale"). Parafrasando in italiano possiamo
riassumere dicendo: "il fatto che socrate è mortale è conseguenza logica del fatto che socrate è un
uomo e della regola che tutti gli uomini sono mortali".
È opportuno precisare subito, anche se si può intuire dì per sé, che le conseguenze logiche derivabili
da un programma sono relative a quanto espresso nel programma stesso, e dipendono quindi dalla
rappresentazione scelta. In riferimento all'esempio precedente, il quesito:
?- greco(socrate). /* socrate è greco? */
ha risposta negativa, cioè non è conseguenza logica del programma, in quanto non è presente in
esso alcun predicato greco con il quale sia possibile la risoluzione del quesito. Il fatto che socrate
fosse greco non è ovviamente falso in assoluto, ma rispetto alla rappresentazione fatta risulta falso,
nel senso di "non dimostrabile".
Il procedimento di confutazione mediante risoluzione consente diverse possibilità nel formulare un
quesito e nell'ottenere delle risposte Consideriamo il seguente esempio:
nonno(X, Z) :- padre(X, Y), padre(Y, Z).
padre(mario, carlo).
padre(carlo, ugo).
Nel seguito si illustreranno le dimostrazioni mediante schemi a più righe ed a tre colonne; nelle
prime due colonne si scriveranno le clausole genitrici e nella terza la sostituzione. Nella prima
colonna della prima riga si scriverà il quesito, nella prima colonna delle righe successive si scriverà

12
la risolvente delle clausole genitrici della riga precedente con la sostituzione indicata. Nell'ultima
riga si avrà la clausola vuota (se la dimostrazione giunge a termine) e la risposta ottenuta.
Come primo caso consideriamo il quesito:
?- nonno(mario, ugo).
Si ha:
:-nonno(mario, ugo)
nonno(X, Z):-
padre(X, Y),
padre(Y, Z)
X/mario, Z/ugo :-padre(mario, Y), padre(Y, ugo)
padre(mario, carlo):-Y/carlo :-padre(mario, carlo), padre(carlo, ugo)
padre(carlo, ugo)_:-
RISPOSTA: sì
Avendo derivato la clausola vuota, la risposta è affermativa:
nonno(mario, ugo) è conseguenza logica delle tre clausole di partenza. Parafrasando in italiano: "è
vero che mario è nonno di ugo" (relativamente alle proposizioni di partenza).

13
2. Operatori
Dove si considerano l'introduzione di operatori per estendere la sintassi standard di Prolog, e la
valutazione di espressioni aritmetiche come meccanismo di computazione aggiuntivo rispetto
all'unificazione.

2.1 Gli operatori in Prolog.


Gli operatori sono, in Prolog, una semplice convenienza notazionale, un'alternativa sintattica per
facilitare l'utilizzo, la manipolazione e la leggibilità degli usuali termini strutturati. È possibile
rappresentare nella sintassi di operatore strutture con 1 o 2 argomenti dichiarando quale operatore
l'atomo usato come funtore principale della struttura, e scrivendo quindi le componenti della
struttura quali argomenti dell'operatore. La sintassi Prolog prevede tre categorie di operatori:
infissi, prefissi e postfissi. Un operatore infisso può venire utilizzato per strutture binarie, e
compare sempre fra i suoi due argomenti; operatori prefissi o postfissi possono essere usati per
strutture unarie, e figurano sempre rispettivamente prima o dopo il loro argomento.
Per esempio, strutture come:
> (X,Y)
riceve('Arianna', 'una visita')
;(H, K)
fatto(lavoro)
appartiene_a(Y, Z)
possono venire più convenientemente scritte come:
X>Y
'Arianna' riceve 'una visita'
H;K
lavoro fatto
Y appartiene_a Z
Chiameremo espressione una struttura il cui funtore è rappresentato come operatore. E da notare
tuttavia che, ad esempio, l'espressione 4 * 3 rappresenta unicamente la struttura *(4, 3) e non il
numero 12. L'espressione verrebbe valutata (nell'esempio, la moltiplicazione verrebbe eseguita)
soltanto se tale struttura fosse passata come argomento in ingresso agli operatori di sistema rivolti
a questo scopo.
Poiché una struttura può avere argomenti che sono a loro volta strutture, quando i suoi funtori sono
rappresentati come operatori si ha un'espressione contenente sottoespressioni. Diventa allora
necessario evitare ambiguità su quali sono le sottoespressioni componenti, definendo precedenza
ed associatività degli operatori.

2.2 Precedenza di un operatore.


A ciascun operatore va associata una precedenza (o priorità), rappresentata da un numero intero
compreso in un intervallo, che cambia da implementazione ad implementazione: in alcuni casi si
estende da 1a 255, in altri da 1a 1200, come nel Prolog/DEC-10 e nel CProlog, od altri ancora; in
tutti i casi, a numeri più piccoli corrisponde una precedenza più alta.
La precedenza serve ad eliminare l'ambiguità di espressioni la cui struttura non è resa esplicita

14
mediante l'uso delle parentesi; la regola generale consiste nel considerare quale funtore principale
l'operatore dotato della precedenza più alta, ovvero con numero di precedenza minore. Così, se "-
" ha una precedenza più bassa di "*", allora le espressioni:
a-b*c
a-(b*c)
sono tra loro equivalenti e rappresentano entrambe la struttura:
-(a, *(b, c))
La forma infissa del termine
*(-(a,b),c)
va scritta con le parentesi:
(a -b) * c
Alle parentesi viene tacitamente assegnato numero di precedenza zero (o negativo), in modo da
prevalere su ogni altro operatore; esse possono quindi essere usate per annullare gli effetti della
precedenza e dell'associatività definite, nonché per esplicitare con maggiore evidenza la struttura.
Se, in una data espressione o sottoespressione, sono presenti due operatori che hanno la stessa
precedenza, l'ambiguità che così si determina può essere risolta definendo l'associatività (o tipo)
degli operatori.

2.3 Associatività di un operatore


L'associatività specifica se l'operatore è infisso, prefisso o postfisso; specifica inoltre come va
interpretata un'espressione contenente più operatori dotati della stessa precedenza. Per definirla si
hanno le seguenti possibilità:
natura dell'operatore tipo

prefisso (unario) fx
fy
infisso (binario) xfx
xfy
postfisso (unario) xf
yf

In questa notazione f rappresenta l'operatore, x e y i tipi degli argomenti, nelle corrispondenti


posizioni. Una x(rispettivamente, una y) rappresenta un argomento che, se contiene operatori, può
contenere solo operatori di precedenza strettamente inferiore (rispettivamente, uguale od inferiore)
a quella di f.
Consideriamo dapprima il caso degli operatori infissi. Con un operatore del tipo xfx i funtori
principali di entrambe le sottoespressioni che costituiscono gli argomenti dell'operatore devono
essere di precedenza più bassa rispetto all'operatore stesso, a meno che la sottoespressione non
risulti racchiusa tra parentesi (il che le assegna precedenza massima). Un operatore del tipo xfx è
dunque non associativo. Con un operatore di tipo xfy, invece, dev'essere di precedenza inferiore la
sottoespressione alla sua sinistra, mentre l'altra potrà essere di precedenza inferiore o uguale a

15
quella dell'operatore principale, che sarà dunque associativo a destra. Il viceversa vale naturalmente
per un operatore del tipo yfx, che risulterà così associativo a sinistra.
Ad esempio, se gli operatori "+ " e " -" sono entrambi di tipo yfx ed hanno lo stesso numero di
precedenza, allora l'espressione:
a-b+c
è corretta e va interpretata come:
(a-b)+c ossia: +(-(a,b),c)
e non come:
a-(b + c) che equivarrebbe a: - (a, +(b, c))
Risulta anche possibile costruire espressioni sintatticamente corrette, che tuttavia non si
conformano alle suddette regole di precedenza e di associatività, e che sono pertanto
potenzialmente ambigue; ne è un esempio l'espressione precedente nel caso che entrambi gli
operatori siano di tipo xfx. In tali situazioni si considera dapprima l'operatore a sinistra, quindi
l'espressione precedente verrebbe interpretata come:
(a-b) +c
Significherebbe invece:
a-(b+c) ossia -(a,+(b,c))
se i tipi fossero entrambi xfy.
Ancora, se l'operatore ">>" è associativo a sinistra, ossia di tipo yfx, e l'operatore "<<"
associativo a destra, di tipo xfy, in base alle regole suddette, implicite nel sistema Prolog:
1 >> 2 >> 3 viene considerato come: (1 >> 2) >> 3
1 << 2 << 3 viene considerato come: 1 << (2 << 3)
1 << 2 << 3 viene considerato come: (1 << 2) << 3

Si noti che lo specificatore x è quello in grado di risolvere l'eventuale ambiguità di un'espressione,


in quanto i termini presenti in x devono sempre avere una precedenza più bassa rispetto
all'operatore f.

Il significato dei tipi ammessi per gli operatori prefissi e postfissi può essere visto per analogia con
quello degli operatori infissi. Per esempio, se not è un operatore prefisso di tipo fy, allora
not G e not not G sono due modi corretti per indicare rispettivamente le strutture not(G) e
not(not(G)). Se invece il tipo è fx, allora not G è ancora corretto, mentre non è accettabile not not
G. Si ha dunque che fy ed yf sono associativi, al contrario di fx ed xf.

16
2.4 Dichiarazione ed uso di operatori.
In Prolog è possibile dichiarare come operatore un funtore di numero di precedenza P, associatività
T e nome N effettuando, mediante una direttiva nel programma, una chiamata al predicato
predefinito op:

:-op(P, T, N).

Per esempio, per definire l'operatore "->" che realizza il costrutto "if-then-else" si dichiara:
op(1050, xfy, ->).
dopo di che si potrà utilizzare l'operatore con tali precedenza e tipo all'interno del programma.
L'argomento N può anche essere una lista di nomi di operatori (eventualmente uno solo) che
vengono dichiarati dello stesso tipo e precedenza, come in:

:-op(300, yfx, [-,%,&]).


:-op(1050, xfy, [->]).

Il predicato op consente di dichiarare più operatori dotati dello stesso nome, ma di differente natura:
infissi, prefissi o postfissi. Un qualsiasi operatore, anche predefinito nel sistema, può venire
ridefinito per mezzo di una nuova dichiarazione, che annulla così tutte le precedenti definizioni,
esplicite od implicite.

L'insieme degli operatori di sistema ed i rispettivi numeri di precedenza variano nelle diverse
versioni di Prolog, mentre non mutano i loro tipi; è dunque possibile formulare il seguente
prospetto, nel quale sono indicati gli operatori principali e le loro precedenze relative, ma in una
scala arbitraria:
:-op(250, fx, [?-]).
:-op(250, xfy, [:-]).
:-op(230, xfy, [;]).
:-op(210,xfy, [,]).
:-op(200, fx, [not ]).
:-op(180,xfy, [.]).
:-op(160, xfx, [is, =.., ==, \==, @<, @>, @=<, @>=, <,>, =:=, =\=, =<, >=, =, \=]).
:-op(140, yfx, [+,-]).
:-op(140, fx, [+,-]).
:-op(120, yfx, [*,/,//]).
:-op(100, xfx, [mod]).

Alcuni di questi operatori sono già stati considerati precedentemente (in particolare, gli operatori
":-" e "," fanno parte della sintassi standard delle clausole.
L'uso degli operatori consente di formulare in maniera espressiva la definizione di relazioni. Si
vedano come esempio le seguenti procedure:
:- op(100, xfx, 'si trova in').
:- op(250, fx, concatenando).
:- op(100, xfx, e).
:- op(200, xfx, 'si ottiene').

17
:- op(250, fx, cancellando).
:- op(100, xfx, da).
Elemento 'si trova in' [Elemento | _].
Elemento 'si trova in' [_ | Coda] :- Elemento 'si trova in' Coda.
concatenando [ ] e L 'si ottiene' L.
concatenando [T | L1] e L2 'si ottiene' [T | L3] :- concatenando L1 e L2 'si ottiene' L3.
cancellando E da [E | Resto] 'si ottiene' Resto.
cancellando E da [T | C1] 'si ottiene' [T | C2] :- E\== T, cancellando E da C1 'si ottiene' C2.
Esempi di quesiti secondo questa sintassi sono:
?-b 'si trova in' [a,b,c].
?-concatenando [a,b] e X 'si ottiene' [a,b,c,d].
?-cancellando 5 da [1,3,5,7] 'si ottiene' X.
La dichiarazione di nuovi operatori fornisce la possibilità di estendere la sintassi standard del
linguaggio, e più generalmente di sviluppare programmi che accettano clausole scritte in una
specifica notazione desiderata; ciò rende molto facile sviluppare un'interfaccia di utente congeniale
ad un particolare utilizzo del linguaggio.

Ricerca del risultato di un'espressione aritmetica.

Il processo di unificazione, descritto in precedenza, non valuta espressioni, bensì semplicemente le


sottopone a successive sostituzioni simboliche: così, se X è istanziata a +(Y, 1), Y a +(Z, 2) e
Z a 3, l'istanziamento finale per X è: +(+(3, 2), 1), e non 6. Il risultato 6 viene ottenuto solo
mediante l'utilizzo del predicato di sistema is, che forza la valutazione delle espressioni.

Il predicato is è definito come operatore infisso. Il suo argomento sinistro deve essere una variabile
(libera od istanziata) od un numero, ed il suo argomento destro un'espressione aritmetica valutabile
od un numero; affinché l'espressione sia valutabile, tutte le variabili che compaiono in essa devono
essere istanziate.

L'invocazione della meta:

Z is X
comporta la valutazione dell'espressione aritmetica X, e riesce o fallisce a seconda che il risultato
possa o meno venire unificato con Z; si determina un errore di esecuzione se X non è un'espressione
aritmetica valutabile, oppure se Z non è una variabile od un numero. La meta non ha effetti
collaterali e non può venire risoddisfatta. Secondo le convenzioni del Prolog/DEC-10, la procedura
is può anche valutare una lista di un elemento istanziato ad un'espressione intera.
Alcuni esempi sono:

X is 4 * 4 riesce ed istanzia X a 16;

1 is 4-3 riesce;

1 is 3*4-2 fallisce;

a is 4 -3 dà errore;

18
1+1 is 4-2 dà errore;

3 is 4 –X fallisce se X è istanziata ad un numero diverso da 1, dà errore se non è istanziata;

5 is 4 + a dà errore;

40 is [40] riesce.

Si noti che la necessità che l'argomento destro di is sia istanziato rende la procedura non reversibile;
più in generale i predicati aritmetici, in quanto forzano la valutazione delle espressioni in deroga
al normale funzionamento dell'unificazione, comportano uno scostamento dalla semantica
dichiarativa usuale. Come esempio consideriamo la seguente ulteriore versione di procedura per il
calcolo del fattoriale:

fattoriale_rapido(Numero, Fattoriale) :-fattoriale_rapido_1(1, Numero, Fattoriale).


fattoriale_rapido_1(N, N, N).
fattoriale_rapido_1(N1, N2, Risultato) :N1 <N2, Medio is (N1 + N2)//2, Medio_1 is Medio +1,
fattoriale_rapido_1(N1, Medio, R1),
fattoriale_rapido_1(Medio_1, N2, R2), Risultato is R1 * R2.

Si tratta di un esempio di procedura deterministica per la quale l'unica risposta possibile viene
trovata al termine del ramo più a sinistra dell'albero di ricerca. La procedura ausiliaria, dopo aver
calcolato i due numeri interi che meglio approssimano il numero medio fra i primi due argomenti,
dà luogo ad una doppia chiamata ricorsiva, i risultati della quale vengono moltiplicati fra loro per
ottenere l'uscita richiesta. La procedura costituisce una buona esemplificazione del metodo di
"divisione e conquista", nel senso che scinde il calcolo in due sottocalcoli indipendenti; se questi
ultimi fossero eseguiti in parallelo, si otterrebbe il risultato più rapidamente, rispetto all
e procedure considerate in precedenza.

2.5 Confronto di espressioni aritmetiche.


I predicati di sistema =:=, =\=, <, >, =<, >= sono definiti come operatori infissi; entrambi i loro
argomenti devono essere numeri od espressioni aritmetiche, altrimenti si determina un errore di
esecuzione. Detto p uno qualsiasi di questi predicati, la meta:
X pY
forza la valutazione delle espressioni aritmetiche X ed Y, e riesce se i risultati stanno fra loro nella
relazione definita da p, altrimenti fallisce; non ha effetti collaterali e non può venire risoddisfatta.
Le relazioni definite da questi predicati sono:
X =:= Y (i valori di X e di Y sono uguali);
X =\= Y (i valori di X e di Y non sono uguali);
X <Y (il valore di X è minore del valore di Y);

19
X > Y (il valore di X è maggiore del valore di Y);
X =< Y (il valore di X è minore o uguale al valore di Y);
X >= Y (il valore di X è maggiore o uguale al valore di Y).
Per esempio:
I1 + 12 + 13 =:= 3 * 12 riesce;
I2*0 =\= 12*1 riesce;
2-3<0 riesce;
4 >= 3 riesce;
15+2<15+1 fallisce.
Gli operatori <, >, =<, >= possono essere utilizzati fra espressioni intere e reali, in una
combinazione qualsiasi. Gli opera- tori =:= e =\= possono essere utilizzati soltanto fra espressioni
intere, in quanto non ha senso sottoporre a verifica di uguaglianza o disuguaglianza dei numeri
reali, poiché la rappresentazione in virgola mobile di un numero non è precisa.

2.6 Operatori logici


Questa sezione fornisce una breve descrizione di due operatori che prendono argomenti che sono
termini di ricorso, intendendo termini che possono essere considerati obiettivi.
Operatore NON
Il prefisso non / 1 può essere posizionato prima di qualsiasi obiettivo per dare il suo diniego. L'obiettivo
negato ha esito positivo se l'obiettivo originale fallisce e fallisce se l'obiettivo originale ha esito positivo. I
seguenti esempi illustrano l'uso di non / 1. Si presume che il database contenga la clausola univoca.

cane(Fido).
- Non cane(fido).
non
? - cane(fred).
non
- Non cane (fred).

X = O, X è 0.
X=0
- X = 0, non X è 0.
non
Operatore di disgiunzione

20
Operatore di disgiunzione; / 2 (scritto come punto e virgola) viene utilizzato per rappresentare
"o". È un operatore infisso che accetta due argomenti, entrambi obiettivi. Obiettivo 1; L'obiettivo
2 ha succede se uno degli obiettivi 1 o 2 ha succede.
?- 6<3;7 is 5+2.
Si
?- 6*6=:=36;10=8+3.
Si

21
3. Loops

Ora parleremo di Looping in PROLOG. Come sappiamo, la maggior parte dei linguaggi di programmazione
convenzionali ha una funzione di loop che consente di eseguire un insieme di istruzioni più volte o finché
non viene soddisfatta una condizione. Sebbene PROLOG non disponga di strutture di loop, gli effetti di tipo
loop possono essere eseguiti in diversi modi, utilizzando backtracking, ricorsione, predicati incorporati o
una combinazione di essi.

3.1 Loop di un numero fisso di volte


A differenza di java, che dispone di funzionalità di loop per consentire l'esecuzione di un set di
istruzioni da un numero fisso di volte, PROLOG non è disponibile per fare questo (direttamente),
ma un effetto simile può essere ottenuto per ricorsione , come ad esempio:
testloop(0).
testloop(N) :- N>0, write(‘Numero: ‘), write(N), nl, M is N-1, testloop(M).

Il predicato testloop è definito come "il ciclo da N, scrivere il valore N, quindi sottrarre uno per
dare M, quindi il ciclo da M". "E con la prima clausola, è definito come" se l'argomento è zero,
non fa nulla (stop!) ".
test del programma:
?- testloop(3).
Numero : 3
Numero :2
Numero :1
Si

3.2 Fare il ciclo fino a quando una condizione è soddisfatta


Questo esempio mostra l'uso del ricorso per leggere l'input dell'utente dalla tastiera e
visualizzarlo sullo schermo finché non si incontra una parola usando la parola = fine.

test :- write(‘Inserisci la parola: ‘), read(parola), write(‘Sei entrato‘), write(parola), nl,


(parola=fine; test).
?- test.
Inserisci la parola: Hello.
Sei entrato Hello
Inserisci la parola: ITS.
Sei entrato ITS
Inserisci la parola: end.
Sei entrato end.
Si

22
3.3 Backtracking con fallimento
Come suggerisce il nome, il predicato fallisce, fallisce sempre, sia sulla valutazione "standard" da
sinistra a destra, sia su backtracking. Combinandolo con il backtrack automatico PROLOG, per
cercare nel database tutte le clausole con una proprietà speciale, possiamo avere un ciclo
automatico.

Questo esempio di programma riportato di seguito è progettato per cercare database contenenti
clausole che rappresentano il nome, l'età, i luoghi di residenza e l'occupazione di un numero di
persone.
Il database contiene i seguenti valori:

persona(John, Smith, 45, London, medico).


persona(Martin, williams, 33, Birmingham, professore).
persona(Henry, Smith, 26, Manchester, idraulico).
persona(jane, wilson, 62, Londra, professore).
persona(Mary, Smith, 29, glasgow, ispettore).
Il predicato persone di seguito è quello di cercare la persona chiamata smith nel database
persone :- persona(Nome, Cognome, _, _, _), Cognome=smith, write(Nome),
write(‘ ‘), write(Cognome), nl, fail.
persone.
Fallimento nel predicato persone viene utilizzato come backtracking, che trova l'intero database
nel programma.

Il risultato del programma sarebbe:


?- persone.
john smith
henry smith
mary smith
si

23
4. Liste
4.1 Notazione
In molti casi è opportuno utilizzare una lista, cioè una sequenza di un numero variabile (da nessuno
ad un qualunque numero) di elementi. Tale numero viene detto lunghezza della lista. Il termine,
che- come si è visto - è l'unico tipo di dato in Prolog, può essere utilizzato per rappresentare una
lista.
Una lista può infatti essere considerata come una struttura che ha o nessuna componente, o due
componenti, chiamate testa e coda, che rappresentano rispettivamente il primo elemento della lista,
e tutti gli altri elementi escluso il primo. Nei due casi, coerentemente con le convenzioni Prolog, si
possono usare come nomi una costante (un funtore a zero posti) ed una struttura con funtore a due
posti.
L'uso frequente delle liste giustifica l'impiego, in Prolog, di due simboli speciali dedicati a questo
scopo: la costante "[]" per denotare la lista vuota, ed il funtore "." (punto) per separare la testa e la
coda della lista. La testa è costituita da un singolo elemento, mentre la coda è a sua volta una lista.
La fine della lista si rappresenta con una costituita dalla lista vuota. Ogni elemento di una lista può
essere rappresentato da un qualunque termine, cioè una costante, od una variabile, od una struttura,
compresa eventualmente un'altra lista.

4.2 Testa e corpo dell'elenco


Si noti che nelle liste l'ordine degli elementi è rilevante, in quanto una lista rappresenta un insieme
ordinato di elementi; perciò, ad esempio, la lista .(a, .(b, [ ])) è diversa dalla lista .(b, .(a, [ ])).
Il funtore "." può essere definito come operatore, cioè si può usare la notazione infissa al posto di
quella prefissa. I casi precedenti possono per esempio essere espressi, rispettivamente, con le
seguenti notazioni infisse:
a.[ ]
a.b.c.[ ]
a.(b.c).d.[ ]
a.X.[ ]
dove le parentesi sono omesse per gli elementi consecutivi perché l'operatore "." è definito come
associativo a destra. Poiché, come si vede chiaramente, rappresentare una lista con il funtore "."
porta spesso ad espressioni difficilmente leggibili nel caso di liste complesse, è definita in Prolog
una sintassi speciale, detta appunto notazione a lista (bracket notation). Questa consiste nel
racchiudere l'intera lista tra parentesi quadre, scrivendo al loro interno gli elementi separati da
virgole (viene omesso il simbolo di lista vuota come elemento di chiusura della lista). I casi
precedenti possono per esempio venire espressi, rispettivamente, con le seguenti notazioni a lista:
[a]

24
[a, b,c]
[a,[b,c],d]
[a,X]
S'intende che anche nella notazione con parentesi quadre, come in quella con funtore ".", la testa
della lista è il primo elemento e la coda consiste della lista contenente ogni altro elemento tranne
il primo. Negli esempi seguenti sono evidenziate, nella notazione a lista, la testa e la coda della
lista:
Lista Testa Coda
[a,b,c] a [b,c]
[a,b] a [b]
[a] a []
[] _ _
[[a,b],c] [a, b] [c]
[a,[b,c]] a [[b,c]]
[a [b,c],d] a [[b,c],d]
[[1,2,3],[2,3,4],[]] [1,2,3] [[2,3,4],[]]

Nella notazione a lista, vi è in Prolog anche un simbolo speciale, "|" (barra verticale), per
evidenziare la suddivisione di una lista in testa e coda: [X|L] (equivalente a .(X,L)) è un termine
che denota una lista la cui testa è l'elemento X e la cui coda è il termine L, che denota una lista. Si
noti che a sinistra di "|" possono comparire più termini, per esempio [X,Y|Z], mentre a destra deve
comparire una lista; così, [X|Y, Z] non è ammesso, mentre [X|[Y|Z]] è possibile, ed è equivalente
a [X,Y|Z] Le seguenti notazioni sono tra loro equivalenti:
[1,2,3]
[1, 2,3| [ ]]
[1,2|[3|[]]]
[1|[2,3|[]]]
[1|[2|[3|[]]]]
Negli esempi seguenti è illustrato il significato associato all'uso delle variabili (comprese le
variabili
anonime "_"):
[X|L] "tutte le liste non vuote"
[ X, Y, Z |[ ]] "tutte le liste di 3 elementi"
[X,Y,Z,Coda] "tutte le liste di 4 elementi"

25
"tutte le liste di 3 elementi nelle quali si ha che
[ X, X, Coda] il primo ed il secondo elemento sono lo stesso
termine"
"tutte le liste di almeno 2 elementi, di cui il
[41,X|Y]
primo è il numero 41"
"tutte le liste di 4 elementi in cui il primo è
l'atomo a, il quarto è l'atomo b, il secondo e il
[a,_,_,b] terzo possono essere indifferentemente o due
qualsiasi termini tra loro diversi o lo stesso
termine"

26
5. Predicati di controllo e di ingresso/uscita
Dove si presentano alcuni predicati di sistema che consentono ulteriori possibilità di specificazione
del controllo, ed i predicati predefiniti per l'ingresso e l'uscita dei dati.

5.1 Predicati predefiniti di controllo.


Tutte le implementazioni di Prolog dispongono di predicati predefiniti di comodo, che sono cioè
utili nella pratica ma risultano o ridondanti, nel senso che comunque il loro effetto può essere
ottenuto usando le usuali caratteristiche del linguaggio, o necessari solo per ragioni di efficienza o
comunque da un punto di vista procedurale. Tra questi vi sono i predicati di controllo descritti in
questo paragrafo, ad eccezione del seguente predicato cali, che ha un ruolo diverso.
Il predicato "call".
Se M è un termine accettabile entro il corpo di una clausola, ossia una meta eseguibile, oppure una
variabile istanziata a tale termine, la meta call(M) viene eseguita esattamente come se il termine M
comparisse testualmente al posto di call(M), e quindi riesce o fallisce secondo che M riesce o
fallisce. Se M è una congiunzione di mete, questa va racchiusa in una coppia dì parentesi
aggiuntive, per evitare ambiguità sul numero di argomenti. Se invece M non soddisfa le condizioni
suddette, la chiamata fallisce (producendo eventualmente un messaggio d'errore).
Ad esempio, date le clausole:
pred(a).
pred(b).
pr(c).
pr(d).
si ha:
?- call(pred(a)). riesce;
?- call(pred(c)). fallisce;
?- call(pred(a), pr(c)). fallisce;
?- call ((pred(a), pr(c))). riesce;
?- call(pred(X)). istanzia X ad a e, con ritorno indietro, a b;
?- call(X). con X istanziata ad esempio a pr(Y), istanziaY a c e, con ritorno indietro, a d.
Il predicato cali trasforma quindi un termine in una meta; esso consente di eseguire una meta non
predeterminata ma variabile dinamicamente a seconda di come è istanziato il suo argomento
(comese si usasse un predicato con funtore variabile). Questo predicato è quindi meta- logico, nel
senso che opera sul linguaggio logico stesso.

27
Equivalentemente, come pura variante sintattica all'utilizzo di cali, una variabile può comparire
essa stessa come meta (in un quesito o nel corpo di una clausola), alle stesse condizioni
dell'argomento di cali. Una variabile che compare in un programma in tale ruolo è detta una
metavariabile, nel senso che sta al posto non di un termine come di solito, ma di un predicato. Ad
esempio, sono equivalenti i seguenti quesiti:
?- Z is 1 + 1.
?- call(Z is 1 + 1).
?- X = (Z is 1 + 1), call(X).
?- X = (Z is 1 + 1), X.
dove "=" è un predicato predefinito che istanzia la variabile a sinistra al termine a destra. La
procedura seguente istanzia la variabile a secondo argomento all'atomo vero in caso di
soddisfacimento della meta che compare al primo, ed a falso in caso contrario:
booleano(Meta, vero) :- call(Meta), !.
booleano(_, falso).
oppure:
booleano(Meta, vero) :- Meta, !.
booleano(_, falso).
La procedura:
una_volta(M) :- call(M), !.
oppure:
una_volta(M) :- M, !.
consente l'esecuzione della meta M in maniera deterministica; costituisce quindi una possibile
realizzazione del predicato once.
Il predicato di disgiunzione ";".
Il predicato di sistema ";" è definito quale operatore infisso che richiede, come argomenti, due
termini che rappresentano mete eseguibili. La chiamata:
G1 ; G2
riesce se la meta G1 riesce oppure, nel caso che G1 fallisca, se la meta G2 riesce; in caso contrario
fallisce. Il predicato ";" esprime quindi la disgiunzione tra le due mete fornite come argomenti. Le
alternative rappresentate, all'interno di una regola, dall'operatore ";" subiscono, nel caso di presenza
del predicato di taglio, il consueto trattamento: dopo il soddisfacimento della meta "!", tutte le
scelte fatte a partire dall'invocazione della meta genitrice vengono automaticamente fissate.

28
Per esempio, la clausola:
a :- b, c, (d, ! ; e, f).
è del tutto equivalente alle tre clausole:
a :- b, c, a_1.
a_1 :- d, !.
a_1 :- e, f.
L'operatore di disgiunzione ";", disponibile in molte versioni di Prolog, rappresenta unicamente un
elemento di convenienza sintattica; infatti il suo uso può sempre venire ovviato o con le seguenti
definizioni generali (usando call o metavariabili):
G1 ; G2 :- call(G1). (oppure G1 ; G2 :- G1.)
G1 ; G2 :- call(G2). (oppure G1 ; G2 :- G2.)
ovvero mediante una differente realizzazione della procedura nella quale compare. Per esempio, la
procedura confronto_insiemi, può venire così riformulata (in modo deterministico) usando
l'operatore ";" per la specificazione delle alternative:
confronto_insiemi(I1, I2, Relazione) :- (sottoinsieme(I1, I2), !, (sottoinsieme(I2, I1), Relazione =
uguali, !; Relazione = sottoinsieme) ; (sottoinsieme(I2, I1), Relazione = sovrainsieme, ! ;
Relazione = 'non confrontabili')). sottoinsieme([ ], _).
sottoinsieme([Elemento | Elementi], Insieme) :- appartenenza(Elemento, Insieme),
sottoinsieme(Elementi, Insieme).
appartenenza(El, [El|_]).
Come si vede, tale formulazione risulta più compatta ma alquanto meno leggibile dell'altra.
I predicati "true" e "fail".
Nel corso della normale esecuzione di un programma Prolog sono possibili il successo od il
fallimento di ogni meta invocata. I predicati true e fail, da utilizzarsi quali procedure senza
argomenti, servono rispettivamente a forzare il successo ed il fallimento della loro meta genitrice.
La meta true riesce ogni volta che viene invocata, non presenta effetti collaterali e non può venire
risoddisfatta in caso di ritorno indietro. Ad esempio, la seguente procedura:
ricerca_numero :- ricerca(X), X>10, nl, write('Numero trovato.') ; true.
usa il predicato ricerca, che si suppone definito altrove, per trovare un numero: se tale numero è
maggiore di 10 viene emessa una segnalazione, in caso contrario viene invocata true; in tal modo
questa digiunzione di mete riesce sempre. La disponibilità del predicato di sistema true è da
intendersi anch'essa come una semplice convenienza offerta all'utente; infatti è sempre possibile
evitarne l'uso con una opportuna ridefinizione. La procedura definita sopra si può per esempio
modificare come segue:

29
ricerca_numero :- ricerca(X), X > 10, nl, write('Numero trovato.').
ricerca_ numero.
Si osservi che la naturale definizione del predicato true consiste semplicemente nella clausola:
true.
La procedura fail fallisce ad ogni sua invocazione, impedendo il successo della congiunzione di
mete in cui compare e forzando di conseguenza l'attivazione del meccanismo di ritorno indietro.
Se non fosse predefinita si potrebbe realizzare semplicemente non rappresentandola con alcuna
clausola nel programma, ovvero sostituendola con qualsiasi meta per la quale non esiste alcuna
clausola, nella base di dati, in grado di soddisfarla. Viene usata essenzialmente nella congiunzione
"!, fail" ed in combinazione con il predicato repeat.
Il predicato "repeat".
Quando viene invocato come meta, il predicato senza argomenti repeat riesce sempre, come true,
ma - al contrario di questo - può venire risoddisfatto un numero arbitrario di volte, generando così
una successione potenzialmente infinita di scelte mediante ritorno indietro. Se non fosse un
predicato di sistema potrebbe essere definito nel modo seguente:
repeat.
repeat :- repeat.
Con questa definizione, all'atto della prima invocazione di repeat viene utilizzata la prima clausola;
successivamente, se la meta repeat viene interessata dal ritorno indietro, la seconda clausola -
utilizzata come alternativa - porta ad una nuova invocazione di repeat, che riesce con l'impiego
della prima clausola, lasciando nuovamente a disposizione - per un eventuale ulteriore ritorno
indietro - la seconda. L'ordine relativo delle due clausole è cruciale: se il fatto fosse preceduto dalla
regola, infatti, si avrebbe un tipico caso di ricorsione infinita. Il predicato repeat è quindi sempre
non deterministico; la sua funzionalità può considerarsi in un certo senso antitetica a quella del
taglio, dal momento che rende disponibili ulteriori rami nell'albero di dimostrazione della meta
principale, anziché eliminarli. Si noti che non è possibile effettuare un'iterazione da N1 a N2 con
una procedura del tipo:
iterazione_sbag1iata(N1, N2) :- repeat, write(N1), M is N1 + 1, (M == N2, ! ; fail).
perché il ritorno indietro distrugge gli istanziamenti, cosicché write scriverà non i valori da N1 a
N2, ma sempre e indefinitamente il valore cui è istanziato N1 al momento della chiamata della
procedura. Il predicato repeat può essere usato solo con al suo interno sottomete che producono
nuovi istanziamenti ogni volta che vengono chiamate.

30
Bibliografia

1. PROLOG Linguaggio e metodologia di programmazione logica, Furlan F., Lanzarone


G.A.
2. Programmazione Dichiarativa in Prolog, CLP e ASP, Agostino Dovier, Andrea
Formisano
3. http://www.ce.unipr.it/research/HYPERPROLOG/esercitazione3.html
4. http://www3.cirsfid.unibo.it/didattica/upload/140_Prolog_01_sintassi.pdf
5. http://www.cs.unibo.it/gabbri/MaterialeCorsi/LucidiProlog.pdf

31

Potrebbero piacerti anche