(define b 1) (define a b) (define prova (lambda (x) (+ 1 pippo))) (define prova2 (lambda (x) (+ x "pippo"))) (define q (+ 1 "pippo")) www.dimi.uniud.it/alessi/sprog.html ======================================= tipi di dato numerico: tipi fondamentali sono: integer, rational, real, complex. Vale la regola per cui un numero di un tipo piu' a sinistra e' utilizzabile da tutte le procedure che lavorono con i tipi piu' a destra. IMPORTANTE: i numeri interi e razionali hanno precisione sostanzialmente illimitata (i numeri razionali vengono rappresentanti come frazioni, ossia come coppie di numeri interi) In particolare entrambi soddisfano la cosiddetta proprieta' di exacteness. (ossia non ci sono margini di approssimazione). Per i numeri reali la situazione e' differente. Si usa una rappresentazione (in genere) a 64 bit, e quindi tutte le relative operazioni che si fanno soffrono di un certo grado di approssimazione. In particolare i numeri reali NON sono esatti. ========================== Precisazione su definizioni "induttive" e "ricorsive". OSS: spesso i due termini si usano come sinonimi senza creare equivoci. E quindi, si trova spesso scritto: la funzione f:N->N fattoriale viene definita induttivamente dalla formula bla bla.... oppure la funzione f:N->N fattoriale viene definita ricorsivamente dalla formula bla bla.... Ricorsivo: di successione di funzioni, formule o dimostrazioni, in cui ogni elemento si puo' ricavare dal precedente. Induzione: procedimento per cui, partendo da dati empirici fra loro uniformi, si arriva alla formulazione di una regola generale. per noi vuol dire questo: procedimento per cui, partendo da casi "base", si arriva alla definizione dei casi generali. La definizioni ricorsive sembrano essere piu' generali di quelle induttive, che sono univoche e precise. Le definizioni induttive usano quasi sempre il "formato ricorsivo". Formato ricorsivo: definire <qualcosa> "a sinistra", ma <qualcosa> compare anche nella espressione di destra che dovrebbe definire <qualcosa> stesso. Esempio di definizione in formato ricorsiva : la funziona fatt:N->N fatt(n) = se n = 0 allora 1 , altrimenti <====== defin fatt(n-1) * n Altro esempio di definizione ricorsiva ma non induttiva: g(n) = se n = 0 allora 1, altrimenti g(n+1) =============================== PROCEDURE RICORSIVE: si definisce una procedura che richiama se stessa nel suo corpo. ESEMPIO: scrivere un programma che calcola la funzione fattoriale (fatt nell' esempio sopra) In realta' una definizione piu' chiara di fattoriale e' questa: fatt(n) = 1*2*3*4.....*n OSS: il fattoriale puo' essere definito anche secondo le "vecchie" e care definizioni fatt(4) = 1*2*3*4 = 24 Per convenzione si assume che fatt(0) sia definito e valga 1. (Questa e' utile per la definizione ricorsiva). Altra definizione (del tutto equivalente) e' quella vista prima: fatt(n) = fatt(n-1) if (n=0) then 1 else n* (define FATTORIALE (lambda (n) (if (= n 0) 1 (* n (FATTORIALE (- n 1)))))) Come viene valutata una espressione che coinvolge una procedura ricorsiva? [meccanismo e' il solito: si procede a sostituire fino a esaurimento casi della procedura) ESEMPIO : valutazione a mano di (FATTORIALE 3) (FATTORIALE 3) ===> ( (lambda (n) <corpo>) 3) ===> (if (= 3 0) 1 (* 3 (FATTORIALE 2))) ) (* 3 (FATTORIALE 2) ) ====> ===> (* 3 ( (lambda (n) <corpo>) 2) ) ====> (* 3 (if (= 2 0) 1 (* 2 (FATTORIALE 1))) ) ) ===> (* 3 (* 2 (FATTORIALE 1))) ====> (* 3 (* 2 ( (lambda (n) <corpo>) 1) ) ) ===> (* 3 (* 2 (if (= 1 0) 1 (* 1 (FATTORIALE 0))) ) (* 3 (* 2 (* 1 (FATTORIALE 0)))) ====> =====> (* 3 (* 2 (* 1 ( (lambda (n) <corpo>) 0 )))) ====> (* 3 (* 2 * (1 (if (= 0 0) 1 (* 0 (FATTORIALE -1))) ) ===> (* 3 (* 2 * (1 1))) ==> )) 6 ============================================ Esercizio: scrivere una procedura che stabilisce (fornendo come risposta true/false) se una stringa costituita da zeri e uno, contiene un numero pari di uno oppure no (nel primo caso deve dare true come risposta). "01101" oss1: i singoli caratteri della stringa vanno scanditi uno a uno. (oss2: niente cicli "while") Proponiamo una soluzione ricorsiva che deve basarsi su: - devono esistere uno o piu' casi base dove la risposta e' immediata; - il caso generale (di una stringa come quella sopra) deve essere scomposto in uno o piu' casi (forse ancora generali) dove pero' la "distanza" dai casi base diminuisce. PER LE STRINGHE: spesso i casi base vengono individuati come quelli relativi alla stringa vuota o a stringhe di un carattere. Casi semplici: sicuramente e' semplicissimo il caso della stringa "" (parity-check "") = true Osserviamo inoltre questo: se abbiamo una stringa non vuota s, sara' di questi due forme possibili s = "0_u" <====== intendo: s inizia con uno 0 e poi prosegue come vuole, il pezzo successivo e' la stringa che abbiamo chiamato u s = "1_v" <====== ossia s inizia con un 1 e poi prosegue con il restante segmento chiamato v. SE s = 01101 s = 0_u dove u = 1101 allora: se s = 0_u (parity-check s) <=====> (parity-check u) se invece s = 1_v (parity-check s) <======> (not (parity-check v)) esempio di questo secondo caso false = (parity-check "10011") = (not ((parity-check "0011") = true) ) = (not (parity-check "011")) = (not (parity-check ("11"))) = (not (not (parity-check "1"))) = (not (not (not (parity-check ""))) = false Sintetizzando: se (parity-check "" ) = true <================ caso base se (parity-check "0_u") = (parity-check u) se (parity-check "1_v") = (not (parity-check v)) Lo schemino sopra costituisce il cuore della procedura parity-check: (char=? (string-ref s 0) #\0) ) corrisponde a chiedersi se s e' del tipo 0_u etc etc (define parity-check (lambda (s) (cond ((string=? s "")) #t) ((char=? (string-ref s 0) #\0) (parity-check (substring s 1))) (else (not (parity-check (substring s 1))))))) ====================================== ESERCIZI per casa: - Scrivere un programma first-a? che stabilisce (risultato: booleano) se il primo carattere di una stringa e' una "a" (minuscola) (first-a? "amico" ) ---> true sulla stringa vuota: decidete cosa preferite: se rispondere false oppure mandare un msg di errore (sotto forma di stringa) - Scrivere un programma detect-a, che stabilisce se in una stringa compaiono delle "a" (detect-a "pranzo") ===> true (detect-a "pollo") ==> false - Scrivere un programma quante-a? che stabilisce quante #\a compaiono in una stringa.