<< pagina principale < ······················ << articoli < ······················

Strutture matematiche e Prolog

di Roberto Ricci

Premessa

Lo stile di pensiero algoritmico, o procedurale, ha un ruolo di rilievo nella matematica. E` quindi importante che l'insegnante lo metta in evidenza anche con esempi classici, come l'algoritmo di Euclide per calcolare il M.C.D. tra due numeri naturali o come quello di Newton per calcolare la radice quadrata di un numero positivo; ma è possibile esplicitarlo anche in diverse situazioni ordinarie semplicemente utilizzando per l'esposizione un linguaggio strutturato.

In matematica è tuttavia assolutamente preminente lo stile di pensiero assiomatico-deduttivo che, dichiarati dei principi e delle regole di inferenza deduttiva, consiste nel produrre nuova conoscenza attraverso l'applicazione di tali regole alla conoscenza già acquisita.

Attualmente l'informatica è generalmente usata da chi si serve della matematica, o la insegna, là dove occorre uno stile di pensiero procedurale; sono ancora poco noti quei linguaggi di programmazione che possono potenziare lo stile assiomatico- deduttivo.

Tra questi linguaggi, detti di programmazione logica, il Prolog comincia tuttavia a diffondersi anche tra chi si serve della matematica o la insegna. Nella programmazione in linguaggio Prolog si può immaginare di avere di fronte un automa, che potremo chiamare automa Prolog, in grado di memorizzare una base di conoscenza e di inferire da esso ulteriore conoscenza. Intenderemo per base di conoscenza un repertorio di fatti e di regole di inferenza che mettono in grado di rispondere, con un si o con un no, a domande in forma di enunciati oppure di trovare i valori che sostituiti alle incognite trasformano un enunciato aperto, cioè una funzione proposizionale, in un enunciato vero.

Nel seguito si vuole mostrare un esempio di programmazione Prolog applicato ad un argomento normalmente affrontato nell'insegnamento della matematica nel biennio: lo studio del concetto di operazione e quindi delle strutture algebriche con una operazione. Risulterà evidente come sia possibile insegnare all'automa Prolog a riconoscere in generale le proprietà delle operazioni, applicandole a insiemi concreti, e soprattutto come tale obiettivo sia di concreto stimolo per un'approfondita analisi dei concetti in esame.

 

Insiemi ed operazioni

Si consideri una operazione definita in un insieme come ad esempio { a, b, c }. In linguaggio Prolog possiamo definire facilmente, per elencazione, sia l'insieme che l'operazione traducendo espressioni comuni come: " a è un elemento dell'insieme ", " a composto con c dà come risultato c ". Se ad esempio l'operazione ha la tabellina pitagorica:

ope a b c
a a b c
b b b c
c c c b

allora potremo scrivere:

elem(a). 
elem(b). 
elem(c). 

per rappresentare l'insieme e

ope(a,a,a). ope(a,b,b). ope(a,c,c). 
ope(b,a,b). ope(b,b,b). ope(b,c,c). 
ope(c,a,c). ope(c,b,c). ope(c,c,b). 

per rappresentare l'operazione, mediante la sua tabellina pitagorica. Innanzitutto dovremo mettere l'automa Prolog nelle condizioni di controllare che ad ogni coppia di elementi dell'insieme sia attribuito un risultato e non più di uno. Dopo qualche tentativo iniziale per far capire all'automa che una operazione deve essere appunto ovunque definita, cioè che ogni coppia di elementi dell'insieme deve "avere un risultato", ci si rende conto della difficoltà causata dal quantificatore universale 'ogni'.

A questo proposito viene in mente che anche agli studenti è spesso difficile servirsi correttamente di una definizione che faccia uso del quantificatore universale 'ogni', e che può essere molto utile ripetere la stessa definizione ricorrendo al quantificatore esistenziale 'esiste' per definire il concetto complementare. Ad esempio:

-un'operazione è non ovunque definita quando esiste almeno una coppia dell'insieme che non "ha risultato"-

In Prolog, tenendo conto che alle lettere maiuscole è assegnato il ruolo di variabili e al trattino '_' la funzione di variabile a cui non serve associare un nome particolare, si può scrivere:

nonOvunqueDefinita :- elem(X) , elem(Y), not ope(X,Y,_). 
ovunqueDefinita :- not nonOvunqueDefinita. 
Inoltre
 
nonUnivoca 
  :- elem(X), elem(Y), ope(X,Y,Ris1), ope(X,Y,Ris2), Ris1 \= Ris2. 
univoca :- 
  not nonUnivoca. 
e quindi
operazione :- ovunqueDefinita, univoca. 
Potremo anche insegnare all'automa Prolog proprietà delle operazioni come l'essere: interna, associativa, commutativa, con elemento neutro, ecc.. fino a definire il concetto di gruppo. Queste sono le proprietà che s'insegnano agli studenti anche nel biennio della scuola media superiore. Con lo stesso stile già utilizzato sopra si può spiegare all'automa Prolog quando un'operazione è interna: -una operazione è non interna quando esiste almeno una coppia di elementi dell'insieme che dà come risultato un elememto non di quello stesso insieme-.
nonInterna :- elem(X),  elem(Y), ope(X,Y,Ris), not elem(Ris). 
interna :- not nonInterna. 
Quanto segue è la descrizione, direttamente in linguaggio comprensibile all'automa Prolog, delle altre proprietà delle operazioni accennate più sopra.
nonAssociativa :- 
  elem(X) , elem(Y) , elem(Z) , ope(X,Y,XY) , ope(XY,Z,Ris) , ope(Y,Z,YZ) , not 
  ope(X,YZ,Ris). 
associativa :- not nonAssociativa. 
nonCommutativa :- elem(X), elem(Y) , ope(X,Y,Ris), not ope(Y,X,Ris). 
commutativa :- not nonCommutativa.
e inoltre:
nonElemNeutro(I) :- elem(X) , not ope(I,X,X). 
nonElemNeutro(I) :-  elem(X) , not ope(X,I,X). 
elemNeutro(I) :- elem(I) , not nonElemNeutro(I). 
simmetrico(X,InvX) :- elemNeutro(I) , ope(X,InvX,I) , ope(InvX,X,I). 
nonSimmetrica :- elem(X) ,  not simmetrico(X,_). 
simmetrica :- not nonSimmetrica. 
Infine:
gruppo :- operazione, interna, associativa, elemNeutro(_), simmetrica. 
gruppoAbeliano :- gruppo ,  commutativa. 
Alcuni esempi In complesso le precedenti definizioni costituiscono una base di conoscenza che mette in grado l'automa Prolog di riconoscere diverse proprietà delle operazioni definite, abilità di cui lo stesso insegnante può utilmente servirsi.

1. Si consideri l'insieme dei valori di verità della logica classica e come operazione la corrispondente del connettivo 'vel'. Nella precedente base di conoscenze sostituiremo le definizioni dell'insieme e dell' operazione con le seguenti:

elem(falso). 
elem(vero). 
ope(falso,falso,falso). 
ope(falso,vero,vero). 
ope(vero,falso,vero). 
ope(vero,vero,vero). 
Dopo ciò si potrà avere con l'automa Prolog il colloquio seguente:

?-gruppo. 
no 
?-elemNeutro(I). 
I=falso More(y/n)?y 
no 
?-simmetrica. 
no 

2. Si considerino gli interi modulo 4 con l'operazione di somma:

elem(0). elem(1).  elem(2). elem(3). 
ope(X,Y,Z) :- elem(X), elem(Y), Z is (X+Y) mod 4. 
In questo caso l'operazione è stata definita sinteticamente mediante un'uguaglianza, rappresentata dal predicato 'is', e dall'operatore predefinito 'mod', che restituisce il resto della divisione intera. Qui, come negli esempi di seguito, si potrà avere un colloquio con l'automa Prolog del tutto simile a quello riportato nel primo esempio.

3. In alcuni elaboratori i numeri interi vengono rappresentati con una struttura finita ciclica in cui i valori sono compresi, ad esempio, tra -32768 e 32767 e il successivo di 32767 è -32768. La struttura additiva che descriviamo è dello stesso tipo ma, per semplicità, con solo quattro valori:

elem(-2). elem(-1). elem(0). elem(1). 
ope(-2,1,-1). ope(-1,1,0). ope(0,1,1). ope(1,1,-2). 
ope(X,Y,Z) 
  :- elem(X), elem(Y), not Y=1, ope(X,1,X1), ope(Y1,1,Y), ope(X1,Y1,Z). 
L'operazione è definita elencando prima di tutto i risultati di x + 1 e poi servendosi della formula ricorsiva: x + y = (x + (y-1)) + 1. E` interessante osservare che la funzione:
0 ® 0
1 ® 1
-2 ® 2
-1 ® 3
è un isomorfismo tra questa struttura e quella descritta nell'esempio 2.

4. Si consideri l'insieme delle trasformazioni in sé di un triangolo equilatero e la solita operazione di composizione. Se rappresentiamo i vertici del triangolo con le lettere 'a', 'b', 'c', possiamo rappresentare le trasformazioni mediante liste

elem([a,b,c]). elem([c,a,b]). elem([b,c,a]). 
il cui significato è descritto da:
trasf(a,[X,_,_],X). trasf(b,[_,Y,_],Y). trasf(c,[_,_,Z],Z). 
e quindi in particolare la trasformazione [a,b,c] è la trasformazione identica. L'operazione di composizione potrà essere descritta nel modo seguente:
ope([X,Y,Z],S,[X1,Y1,Z1]) 
  :- trasf(X,S,X1), trasf(Y,S,Y1), trasf(Z,S,Z1).