(* come si implementa un ciclo? (* funzioni di put e output http://caml.ria.fr/pub/docs/manual-ocaml/libref/pervasives.html Standard put e standard output: val prt_strg : strg -> unit val prt_t : t -> unit... val read_le : unit -> strg val read_t : unit -> t... sequenze di comandi (* ciclo: t -> t -> unit ciclo n m: stampa gli teri da n a m (estremi clusi) let rec ciclo n m = if n>m then () else (prt_t n; (* sequenza di comandi prt_newle(); ciclo (n+1) m) (* ciclo e' "tail recursive": al ritorno dalla chiamata ricorsiva non si deve fare nulla (* ciclo2: t -> unit ciclo2 n: stampa degli teri da 0 a n let ciclo2 = ciclo 0 (* leggi: unit -> t legge una sequenza di lee termata da "." e ne riporta il numero let rec leggi () = let s = read_le () if s="." then 0 else 1 + leggi() (* leggi non e' tail recursive: al ritorno dalla ricorsione si deve ancora aggiungere 1 al risultato (* somma: unit -> t legge una sequenza di teri termata da "." e riporta la somma dei numeri letti let rec somma () = let s = read_le () if s="." then 0 else (t_of_strg s) + somma() (* somma non e' tail recursive (* uso "sporco" delle eccezioni let rec somma2 () = try let n = t_of_strg (read_le()) n + somma2() with _ -> 0 (*strglen: strg -> t (* lunghezza di una strga = Strg.length let strglen s = (* aux: t -> t aux i = numero di caratteri s che vanno dalla posizione i
alla fe della strga. implementa un ciclo. E' tail recursive let rec aux i = try let _ = s.[i] (* serve solo per verificare se s.[i] esiste aux (i+1) with _ -> i (* izializzazione del ciclo: izialmente, i=0 aux 0 (* loop: unit -> t * t riporta numero e somma degli teri letti let s = read_le() if s="." (* termato? then (0,0) (* nessun numero letto, somma 0 else (* strga rappresenta un t, leggi gli altri numeri let (tot,somma) = loop() (tot+1,somma+(t_of_strg s)) (* oppure try let n = t_of_strg(read_le()) let (tot,somma) = loop() (tot+1,somma+n) with _ -> (0,0) (* loop non e' tail recursive (* General put/output functions: http://caml.ria.fr/pub/docs/manual-ocaml/libref/pervasives.html val open_out : strg -> out_channel val output_strg : out_channel -> strg -> unit val close_out : out_channel -> unit val open_ : strg -> _channel val put_le : _channel -> strg val close_ : _channel -> unit (* obiettivo: leggere da file una sequenza di numeri (* teri, termata dalla strga ".", scrivere il numero di teri (* letti, la loro somma, e la media (* media: strg -> unit (* la strga e` il nome del file let rec media file = (* apertura del canale di put let chan = open_ file (* "ciclo" di lettura dei numeri (* riporta numero di teri letti e loro somma (* loop: unit -> t * t let strga = put_le chan (* lettura da file if strga = "." then (* sequenza di comandi beg close_ chan; (* chiusura del canale di put (0,0) end else let (n,somma) = loop ()
(n+1, somma + (t_of_strg strga)) let (n,somma) = loop () (* sequenza di comandi prt_strg ("Letti "^(strg_of_t n)^ " teri\nsomma: "^(strg_of_t somma)^ "\nmedia: "^ (strg_of_float ((float_of_t somma)/.(float_of_t n))) ^"\n") (* niente overloadg (* ----------------------------- (* processi ricorsivi e iterativi let rec fact = function 0 -> 1 n -> n * fact(n-1) (* la funzione non e' tail recursive: implementa un processo ricorsivo (* fact 3 ==> 3 * fact 2 ==> 3 * (2 * fact 1) ==> 3 * (2 * (1 * fact 0)) ==> 3 * (2 * (1 * 1)) ==> 3 * (2 * 1) ==> 3 * 2 ==> 6 Ma il prodotto e' associativo, qudi: 3 * (2 * fact 1) = (3 * 2) * fact 1 e: fact 3 = 3 * fact 2 = (3 * 2) * fact 1 = (6 * 1) * fact 0 = 6 * 1 = 6 Non c'e' bisogno di aspettare il risultato delle chiamate ricorsive, possiamo eseguire subito il calcolo 3*2 e conservarlo un "accumulatore" (o risultato parziale) ==> algoritmo iterativo let rec fact' n = (* aux: t -> t -> t il primo argomento e' il "risultato parziale" let rec aux f = function 0 -> f (* il "ciclo" terma n -> aux (f*n) (n-1) aux 1 n (* il ciclo e' implementato mediante un costrutto ricorsivo. Uso di una funzione ausiliaria che ha un parametro piu', l'accumulatore: i suoi argomenti sono le variabili che vengono "modificate" nel ciclo. La funzione prcipale richiama quella ausiliaria "izializzando" le variabili del ciclo (* il processo e' iterativo: fact' 3 = aux 3 1 = aux(2,3) = aux(1,6) = aux(0,6) = 6 Dopo aver raccolto il risultato della chiamata ricorsiva, non si deve fare nulla.
(* Ma cosa calcola aux (specifica dichiarativa)? (* Un processo ricorsivo: - esegue calcoli al ritorno dalla ricorsione - usa spazio proporzionale alla "dimensione" dell'put. In un processo iterativo: - il risultato parziale viene conservato un accumulatore; - dopo aver faccolto il risultato della chiamata ricorsiva non si deve fare nulla - l'ultima chiamata puo' riportare il suo risultato direttamente alla prima (* ciclo implementa un processo iterativo o ricorsivo? leggi, somma, strglen, loop? (* leggi_it: unit -> t legge una sequenza di lee termata da "." e ne riporta il numero let leggi_it () = let rec aux n = (* n = numero di righe lette fora let s = read_le() if s="." then n (* n e non 0 else aux (n+1) (* cremento del risultato parziale (* i calcoli sono eseguiti PRIMA della chiamata ricorsiva aux 0 (* izializzazione del risultato parziale (* somma_it: unit -> t legge una sequenza di teri termata da "." e riporta la somma dei numeri letti let somma_it () = let rec aux tot = (* tot = somma dei numeri letti fora let s = read_le() if s="." then tot (* tot e non 0 else aux (tot + (t_of_strg s)) (* cremento del risultato parziale aux 0 (* izializzazione del risultato parziale (* loop_it: unit -> t * t riporta numero e somma degli teri letti let loop_it () = (* aux: t -> t -> t * t let rec aux tot somma = (* due "accumulatori" let s = read_le () if s="." then (tot,somma) (* e non (0,0) else aux (tot+1) (somma + (t_of_strg s)) (* cremento degli "accumulatori" aux 0 0 (* uso di loop iterativo media (* media_it: strg -> unit (* media_it s = legge dal file di nome s una sequenza di numeri (* teri, termata dalla strga ".", e stampa il numero di teri (* letti, la loro somma, e la media let rec media_it file = let chan = open_ file let rec loop tot somma = let s = put_le chan if s = "." then beg close_ chan; (tot,somma) end
else loop (tot+1) (somma + (t_of_strg s)) let (n,somma) = loop 0 0 (* <=== izializzazione prt_strg ("Letti "^(strg_of_t n)^ " teri\nsomma: "^(strg_of_t somma)^ "\nmedia: "^ (strg_of_float ((float_of_t somma)/.(float_of_t n))) ^"\n")