Linguaggi Funzionali Parte 1: Un Esempio di Linguaggio

Linguaggi Funzionali
Parte 1: Un Esempio di Linguaggio Funzionale (ELF)
l
l
l
Programmazione Funzionale
Il tipo di dato Lista
Il Nucleo applicativo del Lisp (ELF)
Parte 2: Linguaggi funzionali
l
l
Programmazione tramite funzioni ricorsive
Altre caratteristiche del LISP
Parte 3: Un interprete per ELF in ELF
l
l
l
l
l
l
l
Rappresentazione dell'ambiente
Rappresentazione dell'ambiente funzionale
Rappresentazione delle espressioni
Il meta interprete
Estensione delle espressioni: COND
L'uso delle variabili non locali
Il passaggio dei parametri per nome
Programmazione Funzionale
Linguaggi basati sul concetto di funzione e di applicazione di una funzione ad
argomenti
l
l
linguaggi funzionali
linguaggi applicativi
LISP (acronimo per LISt Processing) risale alla fine degli anni '50, J. Mc Carthy
Il principio
Un programma è costituito da un insieme di definizioni di funzioni.
L'esecuzione del programma comporta il calcolo del valore di una espressione.
<funzione, lista di argomenti>
è un'espressione che prende il nome di applicazione
Esempio: succ(succ(3))
if predicato then espressione1 else espressione2
10/12/2002
1
è un'espressione condizionale
fatt <== if n = 0 then 1 else n * fatt(n - 1)
è una definizione di funzione che contiene
l
l
l
l
costanti
variabili
condizionali
applicazioni di funzioni e predicati
Il tipo di dato Lista
Il tipo di dato lista L su un universo di atomi A è definito da:
- () appartiene ad L;
- (e l) appartiene ad L, se l è una lista ed e è un atomo o una lista
Liste di atomi
(a1 ... an)
Liste di liste/alberi
((a11 a12) ((a211 a212) (a221 a222)))
In LISP sono definiti come atomi sia i numeri, che qualsiasi identificatore preceduto
dal simbolo ' (QUOTE nel gergo LISP).
Esempi: (3 4 5) ('a 'b)
T e NIL sono due atomi che venono usati per rappresentare i valori di verità True e
False
L'atomo NIL rappresenta anche la lista vuota, viene cioè usato come sinonimo di ().
Le funzioni primitive sul tipo di dato lista
l
l
l
E l'insieme degli atomi e delle liste,
O l'insieme di tutti i dati ammessi dal linguaggio
B l'insieme dei valori di verità True e False.
Costruzione
L’operatore CONS permette di definire una lista.
CONS (CONS: E x L -> L)
10/12/2002
2
(CONS e (e 1 ... e n)) = (ee 1 ... e n)
Esempi:
(CONS 3 ()) = (3)(CONS 4 (CONS 3 ())) = (4 3)
(CONS (CONS 3 ()) (CONS 4 ())) = ((3) 4)
Selezione: FIRST
FIRST (FIRST: L -> E)
(FIRST (e l)) = e
Esempi:
(FIRST (CONS 4 (CONS 3 ()))) = 4
(FIRST (FIRST (CONS (CONS 3 ()) (CONS 4 ())))) = 3
Selezione: REST
REST (REST: L -> L
(REST (e l)) = l
Esempi:
(REST (CONS 4 (CONS 3 ()))) = (3)
(FIRST (REST (CONS (CONS 3 ()) (CONS 4 ())))) = 4
I predicati primitivi sul tipo di dato lista
l
B l'insieme dei valori di verità True e False.
I predicati sono funzioni che restituiscono un valore booleano
Test: ATOM
ATOM: O->B
restituisce il valore T se l'argomento è un atomo, altrimenti NIL.
(ATOM ()) = NIL
poiché la lista vuota () è sinonimo dell'atomo NIL.
Test: NULL
10/12/2002
3
NULL: O->B
restituisce il valore T se l'argomento è la lista vuota, altrimenti NIL.
Test: LISTP
LISTP: O->B
restituisce il valore T se l'argomento è una lista, altrimenti NIL
Test: EQ
EQ: AxA->B
restituisce il valore T se i due argomenti sono lo stesso atomo, altrimenti NIL.
Test: EQUAL
EQUAL: LxL->B
Coppie puntate
(CONS 'a 'b) = (a . b)
Funzioni di selezione
l
l
CAR restituisce il primo elemento della coppia puntata "Content of Address
Register"
CDR restituisce il secondo elemento della coppia puntata "Content of Decrement
Register"
Analoghe a FIRST e REST quando hanno gli stessi tipi di argomenti di FIRST e
REST, ma le coppie puntate consentono una gestione della memoria a livello di
locazione.
l
l
CAR = "Content of Address Register"
CDR = "Content of Decrement Register"
Il Nucleo applicativo del Lisp (ELF)
l
l
l
l
l
costanti
variabili
applicazioni di funzioni predefinite
condizionali
applicazioni di funzioni definite dall'utente
Il simbolo ">" denota il segnale di pronto (prompt) dell'interprete.
10/12/2002
4
Costanti
l
l
gli elementi del dominio dei tipi di dati ammessi: numeri, T, NIL, ()
le espressioni precedute dal carattere QUOTE '
Il valore di una costante corrisponde semplicemente all'oggetto da essa denotato.
Esempi: 3, 'a, '(a b c)
>3
3
>
> 'a
a
>
> ' (a b c)
(a b c)
>
Variabili
Identificatore non preceduto dal carattere QUOTE.
>a
"unbound variable"
>
Il valore di una variabile è calcolato nell'ambiente globale definito tramite
l'espressione speciale SETQ
(SETQ variabile espressione)
assegna alla variabile il valore dell'espressione e lo restituisce
> (SETQ a (CONS 3 NIL))
(3)
>a
(3)
>
Useremo SETQ si usa solo per definire l'ambiente globale
Applicazione di funzioni predefinite
(nomefun argomenti)
10/12/2002
5
l
l
nomefun è il nome di una funzione predefinita nel linguaggio,
argomenti è un elenco di espressioni pari al numero degli argomenti della
funzione nomefun
> (CONS 'a ())
(a)
>
> (FIRST (CONS 4 (CONS 3 ())))
4
>
Il meccanismo di valutazione comporta che il calcolo dell'applicazione più esterna
avvenga solo dopo che sono stati calcolati i valori degli argomenti
Condizionali
Un condizionale è un'espressione del tipo
(COND (condizione1 espressione1)...(condizionen espressionen))
in cui condizione1,..., condizionen sono espressioni il cui valore determina la selezione
dell'espressione da valutare per ottenere il valore del condizionale.
Valutazione di un condizionale:
l
l
l
l
Si valuta la condizione1, se il risultato è diverso da NIL allora il risultato del
condizionale è dato dal valore dell'espressione1
Altrimenti si valuta la condizione2; se questa risulta diversa da NIL il risultato
del condizionale è dato da espressione2;
Altrimenti si prosegue finché non si incontra una condizione il cui valore è
diverso da NIL.
Se nessuna delle condizioni produce un valore diverso da NIL il risultato del
condizionale è NIL.
> (SETQ a 3)
3
> (COND ((ATOM a) a) ((LISTP a) (FIRST a))))
3
>
Condizionale con ELSE/OTHERWISE
(COND ((ATOM a) a) ((LISTP a) (FIRST a)) (T 'error)))
Il risultato di questo condizionale è l'atomo error se nessuna delle due condizioni
precedenti è verificata.
10/12/2002
6
In LISP qualunque espressione è una condizione ammissibile, se restituisce un valore
diverso da NIL, la condizione ha successo.
Condizionale nella forma IF-THEN-ELSE
(IF condizione espressione-then espressione-else)
che ha lo stesso significato di
(COND (condizione espressione-then) (T espressione-else))
Applicazione di funzioni: definizione
(DEFUN nomefunzione (parametri) corpofunzione)
l
l
l
nomefunzione è il nome della funzione
parametri sono una lista di variabili che rappresentano i parametri formali della
funzione
corpofunzione è un'espressione che viene valutata al momento dell'esecuzione
della funzione
> (DEFUN fatt (X)
(COND ((LE X 1) 1)
(T (TIMES X (Fatt (SUB1 X))))))
fatt
>
dove LE, TIMES e SUB1 sono degli operatori definiti sul tipo di dato dei numeri
interi
L'associazione tra nomi di funzioni e la loro definizione prende il nome di ambiente
funzionale
Applicazione di funzioni: valutazione
(nomefun argomenti)
Il numero degli argomenti deve essere uguale a quello dei parametri della definizione
della funzione.
Valutazione
1. recuperare i nomi dei parametri e la definizione di funzione nell'ambiente
funzionale;
2. definire un ambiente locale contenente tutte le variabili che si trovano nella lista
dei parametri, cui viene associato il valore ottenuto valutando l'espressione
dell’argomento corrispondente;
3. valutare il corpofunzione usando l'ambiente locale appena definito.
10/12/2002
7
> (DEFUN secondo (l)
(COND ((NULL l) 'errore)
(NULL (REST l)) 'errore)
(T (FIRST (REST l))))))
secondo
>
> (secondo '(a b c))
b
>
Valutazione
1. vengono recuperati nell'ambiente funzionale la lista dei parametri della funzione
(l) ed il suo corpo:
(COND ((NULL l) 'errore) (NULL (REST l)) 'errore) (T (FIRST (REST l)))))
2. viene creato l'ambiente locale in cui alla variabile l viene associato il valore
dell'argomento:
l assume il valore (a b c)
3. viene valutato il corpofunzione:
(COND ((NULL l) 'errore) (NULL (REST l)) 'errore) (T (FIRST (REST l)))))
> (fatt 3)
6
>
In questo caso la valutazione comporta la valutazione di
(COND ((LE X 1) 1) (T (TIMES X (fatt (SUB1 X)))))
l
l
l
usando l'ambiente locale in cui la variabile X ha valore 3.
usando l'ambiente locale in cui alla variabile X ha valore 2.
usando l'ambiente locale in cui alla variabile X ha valore 1.
Programmazione tramite funzioni ricorsive
> (DEFUN memb (a l)
(COND ((NULL l) NIL)
((EQ a (FIRST l)) T)
(T (memb a (REST l)))))
memb
> (memb 4 '(3 4 5))
T
> (memb 6 '(3 4 5))
NIL
>
10/12/2002
8
Programmazione tramite funzioni ricorsive
> (DEFUN membertree (a l)
(COND ((NULL l) NIL)
((ATOM (FIRST l))
(COND ((EQ a (FIRST l))T)
(T (membertree a (REST l)))))
((membertree a (FIRST l)) T)
(T (membertree a (REST l)))))
membertree
> (membertree 4 '(3 (4 6) 5))
T
> (membertree 6 '(3 (4 7) 5))
NIL
>
Programmazione tramite funzioni ricorsive
> (DEFUN conta (a l)
(COND ((NULL l) 0)
((ATOM (FIRST L))
(PLUS (COND((EQ a (FIRST l)) 1)(T 0))
(conta (a (REST l)))))
(T (PLUS (conta a (FIRST l))
(conta a (REST l))))))
conta
> (conta 4 '(3 (4 6) (5 (2 4 7))))
2
>
Programmazione tramite funzioni ricorsive
> (DEFUN sost (x y l)
(COND ((NULL l) NIL)
((ATOM (FIRST l))
(CONS (COND ((EQ x (FIRST l)) y)
(T (FIRST l))))
(sost x y (REST l)))
(T (CONS (sost x y (FIRST l))
(sost x y (REST l)))))
sost
> (sost 4 8 '(3 (4 6) (5 (2 4 7))))
(3 (8 6) (5 (2 8 7))
>
Altre caratteristiche del LISP
Il LISP completato (COMMON LISP):
10/12/2002
9
l
l
l
strutture dati
altri paradigmi di programmazione: CLOS
ambiente di programmazione
Linguaggio interpretato: programmazione interattiva.
Stile di programmazione: raffinamento incrementale.
In LISP sono disponibili sistemi di rappresentazione della conoscenza
l
l
regole di produzione
frames
Meta interprete per il mini-LISP
Si definisce meta-interprete di un linguaggio un interprete per il linguaggio scritto nel
linguaggio stesso.
Un meta-interprete ha due ruoli:
l
l
fornire una semantica operazionale del linguaggio
fornire la base per la realizzazione di un sistema complesso con un prcesso
simile a quello del bootstrapping dei compilatori.
Il mini-LISP include il condizionale IF-THEN-ELSE ed il tipo di dato lista.
Le strutture dati dell'interprete devono rappresentare
l
l
l
l'ambiente
l'ambiente funzionale
le espressioni
Rappresentazione dell'ambiente
Liste associative (liste di coppie puntate)
((var . val) ... (varn . valn))
(defun lookup (a l)
(if (eq l nil) 'error
(if (eq a (first (first l)))
(rest (first l))
(lookup a (rest l)))))
(defun pairlist (l1 l2)
(if (eq l1 nil) nil
(cons (cons (first l1) (first l2))
10/12/2002
10
(pairlist (rest l1) (rest l2)))))
lookup serve per calcolare ill valore di una variabile nell'ambiente
pairlist si usa per la costruzione dell'ambiente a partire dalla lista dei parametri attuali
e dei parametri formali
(defun newenv (vars vals) (pairlist vars vals))
Rappresentazione dell'ambiente funzionale
Anche per l'ambiente funzionale si usano liste associative
((f1 . (args body)) ... (fn . (argsn bodyn)))
(defun fpar-of (fenv nf)
(first (lookup nf fenv)))
(defun body-of (fenv nf)
(2nd (lookup nf fenv)))
(defun 2nd (x) (first (rest x)))
(defun 3rd (x) (first (rest (rest (x)))))
(defun 4th (x) (first (rest (rest (rest x)))))
Rappresentazione delle espressioni
Rappresentazione delle costanti
Le costanti sono rappresentate con il simbolo QUOTE
(QUOTE val)
Selettore
(defun val-of (exp) (2nd exp))
Riconoscitore
(defun is-const (exp)
(if (atom exp) nil
(eq (first exp) 'quote)))
Rappresentazione delle variabili
Le variabili sono rappresentate con atomi
Riconoscitore
(defun is-var (exp) (atom exp))
10/12/2002
11
Rappresentazione delle applicazioni di funzioni primitive
(CONS arg-1 arg-2)
(CAR arg-1)
(CDR arg-2)
Selettori
(defun arg1-of (exp) (first (arg-of exp)))
(defun arg2-of (exp) (2nd (arg-of exp)))
Riconoscitore
(defun is-prim (exp)
(if (eq (first exp) 'first) T
(if (eq (first exp) 'rest) T
(eq (first exp) 'cons))))
Rappresentazione dei condizionali
(IF condizione espressione-then espressione-else)
Predicati che possono apparire nelle condizioni sono
(ATOM arg-1)
(EQ arg-1 arg-2)
(NULL arg-1)
Selettori
(defun if-of (exp) (first (2nd exp)))
(defun predarg1-of (exp) (2nd (2nd exp)))
(defun predarg2-of (exp) (3rd (2nd exp)))
(defun then-of (exp) (3rd exp))(defun else-of (exp) (4th exp))
Riconoscitore
(defun is-cond (exp) (eq (first exp) 'if))
Rappresentazione delle applicazioni di funzione
(nomefunzione argomenti)
Selettori
(defun fn-of (exp) (first exp))
(defun arg-of (exp) (rest exp))
10/12/2002
12
Riconoscitore
(defun is-appl (exp) T)
Dati per una esecuzione del meta-inteprete
(setq fenv1 '((primo . ((X) (first X)))))
(setq env1 '((v . (a b c))))
(setq exp1 '(primo v))
Usando la rappresentazione tramite liste come input non occorre un parser.
Il meta interprete
Il ciclo principale:
(defun elfeval (fenv env exp)
(if (is-const exp)(do-const exp)
(if (is-var exp)(do-indvar env exp)
(if (is-prim exp) (do-prim fenv env exp)
(if (is-cond exp) (do-cond fenv env exp)
(if (is-appl exp) (do-appl fenv env exp)
'error))))))
Valutazione delle espressioni:
(defun do-const (exp)
(val-of exp))
(defun do-indvar (env exp)
(lookup exp env))
(defun do-cond (fenv env exp)
(if (eq (if-of exp) 'atom)
(if (atom (elfeval fenv env (predarg1-of exp)))
(elfeval fenv env (then-of exp))
(elfeval fenv env (else-of exp)))
(if (eq (if-of exp) 'eq)
(if (eq (elfeval fenv env (predarg1-of exp))
(elfeval fenv env (predarg2-of exp)))
(elfeval fenv env (then-of exp))
(elfeval fenv env (else-of exp)))
'error)))
(defun do-prim (fenv env exp)
(if (eq (fn-of exp) 'first)
(first (elfeval fenv env (arg1-of exp)))
(if (eq (fn-of exp) 'rest)
10/12/2002
13
(rest (elfeval fenv env (arg1-of exp)))
(if (eq (fn-of exp) 'cons)
(cons (elfeval fenv env (arg1-of exp))
(elfeval fenv env (arg2-of exp)))
'error))))
(defun do-appl (fenv env exp)
(elfeval fenv
(newenv (fpar-of fenv (fn-of exp))
(elfeval-list fenv env (arg-of exp)))
(body-of fenv (fn-of exp))))
Valutazione di una lista di espressioni
(defun elfeval-list (fenv env lexp)
(if (null lexp) nil
(cons (elfeval fenv env (first lexp))
(elfeval-list fenv env (rest lexp)))))
Estensione delle espressioni: COND
Rappresentazione del condizionale
(COND (cond1 exp1) (condn expn))
Predicati
(defun empty-cond (exp) (null (rest exp)))
Selettori
(defun cond-of (exp) (first (first (rest exp))))
(defun rest-cond (exp) (rest (rest exp)))
(defun exp-of (exp) (2nd (first (first exp))))
Riconoscitore
(defun is-cond1 (exp) (eq (first exp) 'cond))
Riconoscitore dei predicati primitivi
(defun is-pred-prim (exp)
(if (eq (first exp) 'atom) T
(eq (first exp) 'eq)))
Valutazione dei predicati primitivi
(defun eval-pred-prim (fenv env exp)
10/12/2002
14
(if (eq (first exp) 'atom)
(atom (elfeval fenv env (predarg1-of exp)))
(if (eq (if-of exp) 'eq)
(eq (elfeval fenv env (predarg1-of exp))
(elfeval fenv env (predarg2-of exp)))
'error))))
Per la valutazione del condizionale si noti che se nessuna delle condizioni ha valore
diverso da NIL il condizionale vale NIL
(defun do-cond1 (fenv env exp)
(if (empty-cond exp) NIL
(if (is-pred-prim (cond-of exp))
(if (eval-pred-prim
(elfeval fenv env (cond-of exp)))
(elfeval fenv env (exp-of exp))
(do-cond1 (cons 'cond (rest-condexp))))
(if (null (elfeval fenv env (cond-of exp)))
(do-cond1 (cons 'cond (rest-condexp)))
(elfeval fenv env (exp-of exp)))))
L'uso delle variabili non locali
l
l
l
L'operazione di creazione del nuovo ambiente concatena all'ambiente di
chiamata il nuovo ambiente.
Lookup inizia la ricerca dalle definizioni dell'ambiente locale e quindi considera
quelle dell'ambiente di chiamata.
Il campo d'azione è determinato dinamicamente.
(defun do-appl (fenv anv exp)
(elfeval fenv
(newenv1 (fpar-of fenv (fn-of exp))
(eval-list fenv env (arg-of exp))
env)
(body-of fenv (fn-of exp))))
(defun newenv1 (vars vals env)
(if (null vars) env
(cons (cons (first vars) (cons (first vals) NIL))
(newenv1 (rest vars) (rest vals) env))))
Il passaggio dei parametri per nome
Il passaggio di parametri per nome è definito come la sostituzione simultanea di tutte
le occorrenze dei parametri formali nel corpo della funzione con i corrispondenti
parametri attuali.
(defun do-appl1 (fenv env exp)
(elfeval fenv env
10/12/2002
15
(subst (fpar-of fenv (fn-of exp))
(arg-of exp)
(body-of fenv (fn-of exp)))))
La definizione della funzione di sostituzione è la seguente:
(defun subst (flist alist exp)
(if (is-const exp) exp
(if (is-var exp)(subst-var flist alist exp)
(if (is-prim exp) (subst-appl flist alist exp)
(if (is-cond exp) (subst-cond flist alist exp)
(if (is-appl exp) (subst-appl flist alist exp)
'error))))))
L'espressione in cui la sostituzione viene effettuata, è quella che contiene la lista delle
variabili. Ogni variabile, che corrisponde ad un parametro formale, viene rimpiazzata
con l'espressione del corrispondente parametro attuale.
(defun subst-var (flist alist exp)
(if (null flist) 'error
(if (EQ exp (first flist)) (first alist)
(subst-var (first flist) (first alist))))
Negli altri casi la funzione di sostituzione semplicemente viene applicata
ricorsivamente sulle sottoespressioni.
(defun subst-cond (flist alist exp)
(cons (first exp)
(cons (cons (fn-of (if-of exp))
(subst-list flist alist (predarg-of exp)))
(cons (subst-appl flist alist (then-of exp))
(cons (subst-appl flist alist (else-of exp))
NIL)))))
(defun subst-appl (flist alist exp)
(cons (fn-of exp) (subst-list flist alist (arg-of exp))))
(defun subst-list (flist alist explist)
(if (null explist) NIL
(cons (subst flist alist (first explist))
(subst-list flist alist (rest explist)))))
10/12/2002
16