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