Brevissima introduzione al Lisp Giorgio Ausiello, Luigi Laura 2 marzo 2004 Queste pagine costituiscono un riferimento per gli studenti del corso di Informatica Teorica e non hanno nessuna pretesa di completezza riguardo al linguaggio Lisp. Per gli scopi del corso é di interesse mostrare come si possano denire funzioni con il linguaggio Lisp. Da notare che queste pagine sono state scritte con l'idea di fornire allo studente del materiale da usare davanti a un interprete Lisp, ed é per questo che cominciamo presentando l'interprete Lisp come una calcolatrice interattiva, per consentire al lettore di familiarizzare prima con la forma pressa, poi incrementalmente passare ad argomenti sempre piú complessi. 1 Tipi di dato in Lisp In Lisp vi sono i seguenti tipi di dato base: S-espressioni (o espressioni simboliche o espressioni). Puó essere un Atomo, una Lista o una collezione di S-espressioni racchiuse tra parentesi. é equivalente alla lista vuota, quindi é sia un atomo che una lista. Atomo. É una stringa di caratteri che comincia per una lettera, una cifra o un qualsiasi carattere speciale diverso dalla parentesi aperta ( o chiusa ). Distinguiamo in Atomo numerico o numero (per esempio 10 o 1.41). Atomo simbolico o simbolo (per esempio A, CIAO, +, RKO281) Lista. É una parentesi aperta (, seguita da zero o piú atomi o liste, seguita da una parentesi chiusa ). Un programma Lisp é una S-espressione P al quale l'interprete lisp risponde restituendo una S-espressione che rappresenta il valore di P. 2 L'interprete Lisp come una calcolatrice Forma pressa Nel Lisp le espressioni sono rappresentate mediante la forma pressa: (+ 8 3) corrisponde a 8+3 e l'interprete Lisp restituisce correttamente 11. Nella forma pressa il primo elemento di una parentesi é l'operatore, e i successivi (da destra verso sinistra) sono gli operandi. Nel seguito scriveremo le espressioni Lisp in una riga e quello che viene restituito dall'interprete nella successiva, ad esempio: 1
(* 4 2) 8 (- (* 4 2) (+ 3 2)) 3 Tipi numerici di dato: oat, integer e ratio Il Lisp ci fornisce tre tipi numerici di dato. Consideriamo la seguente divisione tra numeri interi (integer): (/ 8 4) 2 Nell'esempio precedente il risultato e' un numero intero (che ci viene restituito dall' interprete Lisp). Se, viceversa, il risultato non é un numero intero il Lisp restituisce un ratio, ovvero un rapporto: (/ 5 2) 5/2 Con l'istruzione FLOAT possiamo forzare il Lisp a restituirci il valore reale (oat) di un rapporto: (FLOAT (/ 5 2)) 2.5 Per ottenere un risultato equivalente possiamo scrivere i numeri direttamente nel formato oat, come nel seguente esempio: (/ 5.0 2.0) 2.5 Operazioni matematiche Tra le primitive fornite dal Lisp segnaliamo le seguenti operazioni matematiche: 2
(MAX 7 6 8) Massimo (ROUND 1.25) Arrotondamento 8 1 (MIN 7 6 8) Minimo (ROUND 1.75) 6 2 (ABS -8) Valore assoluto (TRUNCATE 14 4) Divisione tra interi 8 3 (EXPT 2 3) Elevamento a potenza (REM 14 4) REMainder: resto della 8 2 divisione tra interi (SQRT 25) Radice quadrata 5 Notiamo che ROUND restituisce il numero intero il cui valore piú si avvicina a quello dell'argomento (di tipo oat). Se X é intero vale la seguente: X=(ROUND (FLOAT X)) Se X,Y sono interi e Y diverso da 0 vale la relazione: (+ (* (TRUNCATE X Y) Y) (REM X Y)=X Il numero di operandi varia (naturalmente) da funzione a funzione: N: +,*,-, MAX, MIN 2: TRUNCATE, REM, EXPT 1: ABS, SQRT, FLOAT 3 Denizione di funzioni numeriche In Lisp si distingue tra programmi, funzioni e primitive. Un programma é un insieme di funzioni (o di denizioni di funzione), mentre le primitive sono le funzioni fornite dal linguaggio Lisp. Per denire una procedura si usa il comando DEFUN che ha la seguente sintassi: (DEFUN <nome procedura> (<lista parametri>) <corpo procedura>) Per esempio posso denire la seguente procedura MODULO che prende in ingresso le due componenti x e y di un vettore bidimensionale e ne restisce il modulo (= x 2 + y 2 ): (DEFUN MODULO (X Y) (SQRT (+ (* X X) (* Y Y)))) MODULO 3
la procedura appena denita puó essere chiamata come al solito con la notazione pressa, ovvero: (MODULO 3 4) 5.0 Nel denire funzioni si puó usare la ricursione, come nel seguente (classico) esempio del calcolo del fattoriale di un numero: (DEFUN FATTORIALE (N) (COND ((= N 1) 1) (T (* N (FATTORIALE (- N 1)))))) Nella prossima sezione deniremo formalmente la sintassi della primitiva COND che abbiamo usato nella denizione della funzione fattoriale. Esercizi 1. Scrivere in forma pressa la seguente espressione: ((3 + 2) 5 + (4 2)) ((2 + 3)/5) 2. Scrivere una funzione Lisp che prende due numeri (positivi) e restituisce la radice quadrata del minore dei due. 3. Scrivere una funzione Lisp ALCUBO che prende un numero e lo eleva al cubo. 4 Atomi, liste e funzioni su liste Prima di introdurre le liste, tipo di dato fondamentale nel linguaggio Lisp (l'acronimo Lisp sta per list processor!) vediamo due primitive fondamentali: SETQ e QUOTE che consentono, rispettivamente, di assegnare valori a variabili e di impedire la valutazione di una espressione. L'istruzione SETQ L'istruzione SETQ prende in ingresso 2 operandi ed assegna al primo il valore del secondo: (SETQ PIGRECO 3.14) 3.14 A questo punto PIGRECO vale 3.14. La seguente istruzione: (SETQ A PIGRECO) 3.14 assegna correttamente ad A il valore di PIGRECO, e quindi 3.14. Se vogliamo fare piú assegnazioni insieme, possiamo scrivere semplicemente: (SETQ PIGRECO 3.14 RADICEDIDUE 1.41 A PIGRECO) 3.14 4
Infatti SETQ accetta n coppie di operandi ed assegna a ogni primo elemento di una coppia il valore del secondo elemento corrispondente. Notiamo che se l'interprete Lisp esegue una serie di operazioni (in questo caso assegnazioni) restituisce sempre l'ultimo valore. Inne notiamo che, ora che abbiamo assegnato a PIGRECO il valore 3.14, se chiediamo all'interprete di valutarlo, esso ci restituirá il suo valore: PIGRECO 3.14 L'operatore QUOTE Abbiamo visto che nella notazione pressa il primo elemento di una lista é l'operatore, e gli altri sono gli operandi. Ma se voglio avere una lista come operando? Come faccio a far capire all'interprete Lisp che la lista che si trova davanti é l'operando e non un qualcosa da valutare? In altre parole, come distinguo (+ 4 2) dalla lista composta dai 3 elementi +,4 e 2? Questa funzione viene assolta dall'operatore QUOTE (apice): (SETQ CLASSIFICA (QUOTE (LAZIO MILAN JUVE))) (LAZIO MILAN JUVE) Solitamente l'operatore QUOTE viene abbreviato con l'apice (') davanti alla parentesi, quindi la seguente istruzione é equivalente alla precedente: (SETQ CLASSIFICA '(LAZIO MILAN JUVE)) (LAZIO MILAN JUVE) Notiamo la dierenza tra le seguenti istruzioni: (SETQ A 'B) B (SETQ C A) B (SETQ C 'A) A Assegna ad A il valore B Assegna a C il valore di A, quindi B Assegna a C il valore A Operazioni su liste: CAR e CDR L'operatore CAR restituisce il primo elemento di una lista: (CAR '(A B C)) A Una lista puó contenere delle liste al suo interno. Per esempio: (CAR '((A B)(C D)) (A B) L'operatore CDR restituisce la lista privata del primo elemento: (CDR '(A B C)) (B C) 5
(CDR '((A B)(C D))) ((C D)) Bisogna prestare particolare attenzione al fatto che CDR restituisce sempre una lista, mentre CAR restituisce un elemento (che puó anche essere una lista). Da notare anche: (CAR ') (CDR ') CAR e CDR possono essere combinati tra di loro (no a un massimo di 4 volte) CDDR é il CDR del CDR. Altri esempi: (CADR '(1 2 3 4)) ;Equivale a (CAR (CDR '(1 2 3 4))) 2 (CDAR '((1 2) (2 3) (3 4))) ;Equivale a (CDR (CAR '((1 2) (2 3) (3 4)))) (2) Costruttori di liste In Lisp ci sono tre primitivi che consentono di costruire delle liste: LIST,APPEND e CONS. LIST prende n operandi e restituisce la lista i cui elementi sono gli n operandi: (LIST 'A) (A) (LIST 'A 'A) (A A) (LIST 'A (CDR '(A B C))) (A (B C)) APPEND prende n liste e restituisce la lista composta dagli elementi delle n liste: (APPEND '(1 2) '(3 4)) (1 2 3 4) (APPEND '(1 2) '(CDR (1 2))) (1 2 CDR (1 2)) CONS prende due operandi: un elemento e una lista e restituisce la lista con lelemento inserito al primo posto: (CONS 'A '(B C)) (A B C) 6
(CONS 'A (CDR '(B C))) (A C) Altre operazioni su liste: REVERSE, LENGTH, SUBST e LAST REVERSE prende come argomento una lista e restituisce la lista in cui gli elementi di livello piú elevato sono invertiti: (REVERSE '(1 2)) (2 1) (REVERSE '(1 2 (3 4))) ((3 4) 2 1) LENGTH conta il numero di elementi di livello piú elevato presenti in una lista: (LENGTH '(A B)) 2 (LENGTH '(A B (C D))) 3 Notiamo che sia REVERSE che LENGTH agiscono sugli elementi di livello piú elevato in una lista, e che considerano atomi e liste allo stesso modo. Quindi LENGTH su una lista composta da 3 atomi restituirá 3, e lo stesso fará su una lista composta di 3 liste. SUBST ha il seguente formato: (SUBST <new expression> <old atom> <expression to substitute>) alcuni esempi: (SUBST 'A 'B '(A B C)) (A A C) (SUBST 'B 'A '(A B C)) (B B C) (SUBST 'A 'B '(B ((B) B) A C)) (A ((A) A) A C) LAST restituisce una lista che contiene l'ultimo elemento della lista datagli come argomento (notare la differenza con CAR, che restituisce un elemento): (LAST '(A B C)) (C) (LAST '((A) (B) (C))) ((C)) 7
(LAST 'A) ERRORE! Denizione di procedure su liste Come abbiamo visto per funzioni numeriche, con la stessa sintassi (DEFUN) é possibile denire nuove procedure che operano su liste. Per esempio deniamo la funzione RUOTASX che prende una lista in ingresso e la ruota a sinistra, ovvero prende il primo elemento e lo inserisce in fondo alla lista. (DEFUN RUOTASX (L) (APPEND (CDR L) (LIST (CAR L)))) RUOTASX Le funzioni appena denite possono essere usate nella denizione di nuove funzioni, se vogliamo scrivere la funzione che ruota a destra una lista, (RUOTADX) possiamo sfruttare la funzione RUOTASX appena denita. L'idea é la seguente: ruotare una lista a destra é equivalente a invertire una lista, ruotarla a sinistra e invertirla di nuovo. Notiamo che se avessimo voluto usare la funzione LAST per prendere l'ultimo elemento e inserirlo in cima avremmo poi avuto il problema di eliminarlo dalla lista originale, cosa che in RUOTASX veniva svolta da CDR. (DEFUN RUOTADX (L) (REVERSE (RUOTASX (REVERSE L)))) RUOTADX Le procedure Lisp che abbiamo visto nora restituivano in uscita un solo valore (o dato). Questa é una caratteristica del Lisp, e per avere piú di un valore in uscita da una procedura si puó far restituire una lista contenente i valori. Predicati Nel linguaggio Lisp esistono due costanti a cui sono associati i valori booleani vero e falso. Il valore falso é rappresentato da, mentre il valore vero da T, anche se per denizione é vera qualsiasi cosa diversa da. Esistono numerose procedure, chiamate predicati, che restituiscono valori di veritï ½ Tra queste: ATOM verica che il suo argomento sia un atomo: (ATOM '(A B C)) (ATOM 'A) Allo stesso modo LISTP restituisce T se il suo argomento é una lista: (LISTP '(A B C)) T (LISTP 'A) 8
A parte ATOM parecchi predicati niscono per P, mnemonico per predicato. Tra questi i seguenti predicati restituiscono T se l'argomento é: NUMBERP un numero ZEROP 0 ODDP (un numero) dispari EVENP (un numero) pari MINUSP (un numero) minore di 0 MEMBER prende in ingresso un elemento e una lista e restituisce T se la lista contiene l'elemento: (MEMBER 'A '(B B A C)) T (MEMBER 'A '(B B C)) Il predicato EQUAL verica che i suoi (2) argomenti siano uguali. Abbiamo quindi: (SETQ A 'B) ; Assegno ad A il valore 'B B (EQUAL A A) T (EQUAL A 'B) T (EQUAL A B) Tra le primitive del linguaggio Lisp ci sono anche gli operatori logici AND, OR e NOT. Alcuni esempi: (AND T ) (OR T ) T (NOT T) Prendere decisioni nelle funzioni: la primitiva COND Adesso che abbiamo visto i predicati vediamo come usarli all'interno delle funzioni che scriviamo. Nella denizione della funzione fattoriale avevamo visto utilizzata la primitiva COND. La sintassi é la seguente: 9
(COND (test1 azione1) (test2 azione2)... (testn azionen)) Il comportamento di COND é il seguente: viene valutato il test1, se vale T viene eseguita l'azione1, altrimenti si passa al test2 e cosí via nche non si trova (se si trova) un test che restituisce T. In tal caso si esegue l'azione corrispondente e poi si esce. Per esempio la funzione FIBONACCI puó essere denita nel modo seguente: (DEFUN FIBONACCI (N) (COND ((= N 0) 1) ((= N 1) 1) (T (+ (FIBONACCI (- N 1))(FIBONACCI (- N 2)))))) 5 Funzioni come parametri di funzioni Supponiamo di voler scrivere una funzione che si comporti nel seguente modo: prenda in ingresso una funzione F e un altro argomento X, e applichi due volte la F su X (ovvero F(F(X))). Denisco la funzione nel seguente modo: (DEFUN DUEVOLTE (F X) (F (F X))) ; Sbagliata! DUEVOLTE A questo punto l'istruzione (DUEVOLTE SQRT 81) ERRORE restituisce errore perché la variabile SQRT non ha valore (per vederla come funzione dovrei farla precedere da una parentesi). Allo stesso modo (DUEVOLTE 'SQRT 81) ERRORE restituisce errore perché 'SQRT viene visto come una costante (quale é!). Per poter chiamare funzioni (incognite nel momento in cui deniamo una funzione) ci serve l'operatore FUN- CALL, che ha la sintassi: FUNCALL <nomefunzione> <operandi della funzione> Quindi la corretta denizione della funzione DUEVOLTE é: (DEFUN DUEVOLTE (F X) (FUNCALL F (FUNCALL F X))) DUEVOLTE da cui 10
(DUEVOLTE 'SQRT 81) 3.0 La funzione SQRT in questo caso operava su un solo elemento. Supponiamo di voler eettuare una somma (o sottrazione) di N elementi, contenuti in una lista. Come facciamo a passarli come parametri a FUNCALL? Supponiamo che gli N elementi siano nella lista L, bisognerebbe scrivere qualcosa del tipo: (FUNCALL '+ (CAR (CDR L)) (CAR (CDR (CDR L))).. (CAR (CDR (..CDR L))) In questo caso si usa la primitiva APPLY, che prende in ingresso una funzione e una lista e chiama la funzione sugli elementi della lista (se volevo chiamare la funzione sulla lista usavo FUNCALL!): (APPLY '+ L) é equivalente a (se L1..Ln sono gli elementi di L): (+ L1 L2.. Ln). 6 Esercizi Esercizio 1 Scrivere la funzione Lisp INVERSA (equivalente alla funzione predenita REVERSE) che, presa in ingresso una lista, restituisca la lista invertita. Soluzione (DEFUN INVERSA (L) (COND ((NULL L) L) (T (APPEND (INVERSA (CDR L)) (LIST (CAR L)))))) Esercizio 2 Scrivere la funzione Lisp APPARTIENE (equivalente alla funzione predenita MEMBER) che, presi in ingresso un atomo A e una lista L, restituisca T se la lista contiene l'atomo. Soluzione (DEFUN APPARTIENE (A L) (COND ((NULL L) ) ((EQ (CAR L) A) T) (T (APPARTIENE A (CDR L))))) Esercizio 3 Scrivere una funzione Lisp che, ricevute in ingresso due liste rappresentanti insiemi, restituisca la lista rappresentante l'unione dei due insiemi. Soluzione (DEFUN UNIONE (L1 L2) (COND ((NULL L1) L2) ((MEMBER (CAR L1) L2) (UNIONE (CDR L1) L2)) (T (CONS (CAR L1) (UNIONE (CDR L1) L2))))) Esercizio 4 Scrivere una funzione Lisp che, ricevute in ingresso due liste rappresentanti insiemi, restituisca la lista rappresentante l'intersezione dei due insiemi. 11
Esercizio 5 Scrivere una funzione Lisp che, ricevute in ingresso due liste rappresentanti insiemi, verichi se i due insiemi coincidono (ovvero se le due liste contengono gli stessi elementi a meno dell'ordine degli stessi).per esempio, se L1=(A B C D)e L2=(B C D A), allora i due insiemi coincidono. Esercizio 6 La funzione REVERSE inverte solo gli elementi di primo livello di una lista. Per esempio, (REVERSE '((1 2) 3 4)) restituisce (3 4 (1 2)). Scrivere la funzione Lisp INVERTITUT- TO che, presa in ingresso una lista, restituisca la lista invertita anche in profonditá Per esempio, (INVERTITUTTO '((1 2) 3 4)) deve restituire (3 4 (2 1)). Soluzione (DEFUN INVERTITUTTO (L) (COND ((NULL L) L) ((ATOM L) L) (T (APPEND (INVERTITUTTO (CDR L)) (LIST (INVERTITUTTO (CAR L))))))) Esercizio 7 La funzione LENGTH conta gli elementi di primo livello di una lista. Per esempio, (LENGTH '((1 2) 3 4)) restituisce 3. Scrivere la funzione Lisp CONTAATOMI che, presa in ingresso una lista, restituisca il numero di atomi contenuti nella lista. Per esempio, (CONTAATOMI '((1 2) 3 4)) deve restituire 4. Esercizio 8 Scrivere la funzione Lisp APPLICA che, prese in ingresso una funzione f e una lista L=(x 1 x 2... x n ), restituisca la lista L'= (f(x 1 ) f(x 2 )... f(x n )). Esercizio 9 [Esonero maggio 1993 - Compito A] Scrivere un programma LISP che, letta una lista contenente una rappresentazione di un polinomio p(x) a coecienti razionali, fornisca in uscita la lista che rappresenta il polinomio p (x) p(x)/ x, derivata prima di p(x). Si denisca una tecnica adeguata per la rappresentazione di polinomi. Esercizio 10 [Esonero maggio 1993 - Compito B] Scrivere un programma LISP che, letta una lista contenente una rappresentazione di un polinomio p(x) a coecienti razionali, fornisca in uscita la lista che rappresenta un polinomio q(x) primitiva di p(x), cioï ½tale che: p(x)dx = q(x). Si denisca una tecnica adeguata per la rappresentazione di polinomi. Esercizio 11 [Esonero maggio 1994 - Compito A] Scrivere una (meta-)funzione Lisp che, prese in ingresso due funzioni g : N N e h : N 3 N restituisca la funzione f denita da g e h per ricursione primitiva. { f(x, 0) = g(x) f(x, y + 1) = h(x, y, f(x, y)) Esercizio 12 [Esonero maggio 1994 - Compito B] Scrivere una (meta-)funzione Lisp che, presa in ingresso una funzione g : N 2 N, restituisca la funzione f denita da g per minimalizzazione: f(x) = µ y {g(x, y) = 0} Esercizio 13 [Esonero maggio 1996 - Compito A] Scegliere un' opportuna rappresentazione degli alberi binari mediante liste e scrivere un programma LISP che, ricevuto in ingresso un albero binario A, verichi se A é un albero binario di ricerca. 12
Esercizio 14 [Esonero maggio 2001] Scrivere un programma LISP che, ricevute in ingresso due liste rappresentanti due polinomi, restituisca la lista che rappresenta la somma dei polinomi. Esercizio 15 [Esonero maggio 2002 - compito A] Scegliere un' opportuna rappresentazione degli alberi binari mediante liste e scrivere un programma LISP che, ricevuto in ingresso un albero binario di ricerca A, restituisca la lista ordinata degli elementi dell'albero. Per esempio, nell'albero riportato in gura 1, il programma dovrebbe restituire la lista L = (3 7 9 12 15). 12 7 15 3 9 Figura 1: Esempio di albero binario di ricerca Esercizio 16 [Esonero maggio 2002 - compito B] Scegliere un' opportuna rappresentazione degli alberi binari mediante liste e scrivere un programma LISP che, ricevuto in ingresso un albero binario A, restituisca la lista delle coppie (elemento, livello) per ogni elemento dell'albero A. Per esempio, nell'albero riportato in gura 2, il programma dovrebbe restituire la lista L = ( (9 0) (22 1) (11 1) (10 2) (9 2) ) (non necessariamente in quest'ordine). Esercizio 17 [Esonero maggio 2003 - compito A] Si consideri il seguente problema: Vertex Cover (VC): Dato un grafo G = (V, E) e un intero k 0 esiste un vertex cover V V, V k, tale che, per ogni arco e = (v 1, v 2 ) E, almeno uno tra v 1 e v 2 sia contenuto in V. Scegliere un' opportuna rappresentazione di gra mediante liste e scrivere un programma LISP che, ricevuti in ingresso un grafo G = (V, E) (nella rappresentazione scelta) ed un insieme di vertici V V, verichi se V ï ½un vertex cover di G. Esercizio 18 [Esonero maggio 2003 - compito B] Si consideri il seguente problema: Independent Set (IS): Dato un grafo G = (V, E) e una costante k 0 esiste V V, con V = k, e tale che per ogni coppia u, v V (u, v) / E? Scegliere un' opportuna rappresentazione di gra mediante liste e scrivere un programma LISP che, ricevuti in ingresso un grafo G = (V, E) (nella rappresentazione scelta) ed un insieme di vertici V V, verichi se V ï ½un independent set di G. 13
9 22 11 10 9 Figura 2: Esempio di albero binario Esercizio 19 [Esame 7 luglio 2003 - compito A] Scrivere un programma LISP che, ricevuta in ingresso una lista di liste, restituisca la lista contenente l'intersezione delle liste. Esempio: se L=((A B C) (B C) (B C D)) il programma deve restituire L'=(B C). Esercizio 20 [Esame 7 luglio 2003 - compito A] Scrivere un programma LISP che, ricevuta in ingresso una lista di liste, restituisca la lista contenente l'unione delle liste. Esempio: se L=((A B C) (B C) (B C D)) il programma deve restituire L'=(A B C D). Esercizio 21 [Esame 15 luglio 2003 - compito A] Si consideri il seguente problema. Set Cover (SC): Dati un insieme S, una collezione C di sottoinsiemi di S C = {c 1, c 2,..., c m } e una costante k, decidere se esiste un sottoinsieme C C tale che C k e C copre S, ovvero S = ci C. Scrivere un programma LISP che, ricevute in ingresso una lista L rappresentante l'insieme S e una lista C di sottoinsiemi c 1, c 2,..., c m (rappresentati come liste) verichi se C è un set cover di L. Per esempio, se L =(A B C D E F) e C =((A B C)(B E F)(D E)), allora C è un set cover di L. Esercizio 22 [Esame 15 luglio 2003 - compito B] Si consideri il seguente problema. Hitting Set (HS): Dati un insieme S, m sottoinsiemi C 1, C 2,..., C m di 2 S e una costante k, decidere se esiste un sottoinsieme S S tale che S k e S contiene almeno un elemento di ciascun C i, i = 1, 2,..., m. Scrivere un programma LISP che, ricevute in ingresso una lista L di sottoinsiemi C 1, C 2,..., C m (rappresentati come liste) e una lista che rappresenta S, verichi se S è un hitting set di L. Per esempio, se L =((A B) (B C D) (E F)) e S =(B E), allora S è un hitting set di L. Esercizio 23 [Esame 9 settembre 2003 - compito A] Scrivere un programma LISP che, ricevuta in ingresso una lista di liste L, rappresentante una matrice quadrata di dimensione n n, restituisca una lista contenente gli elementi sulla diagonale principale. Per esempio, se L = ((A B B) (B C D) (E F F)), il programma deve restituire la lista (A C F). Esercizio 24 [Esame 9 settembre 2003 - compito B] Scrivere un programma LISP che, ricevuta in ingresso una lista di liste L, rappresentante una matrice quadrata di dimensione n n, restituisca una lista contenente 14
gli elementi sulla diagonale secondaria. Per esempio, se L =((A B B) (B C D) (E F F)), il programma deve restituire la lista (B C E). Esercizio 25 [Esame 12 gennaio 2004 - compito A] Scrivere un programma LISP che, ricevute in ingresso due liste rappresentanti insiemi, restituisca la lista rappresentante l'insieme ottenuto come prodotto cartesiano dei due insiemi. Esempio: se L1=(A B C) e L2=(1 2) il programma deve restituire L'=((A 1)(A 2)(B 1)(B 2)(C 1)(C 2)) (non necessariamente in quest'ordine). Esercizio 26 [Esame 12 gennaio 2004 - compito B] Scrivere un programma LISP che, ricevute in ingresso due liste rappresentanti insiemi, restituisca la lista rappresentante l'insieme ottenuto come dierenza simmetrica dei due insiemi. Esempio: se L1=(A B C D) e L2=(E D C F) il programma deve restituire L'=(A B E F) (non necessariamente in quest'ordine). (Ricordiamo che (A, B) = A B A B). 15