(******************************************************************************) (******************************************************************************) (**** Guião 4 ***) (**** ***) (******************************************************************************) (******************************************************************************) open Scanf open List (**** Excepções ***) exception Minha of string;; let rec fact ?(a=1) v = if v < 0 (** Se algo de anormal advém do parametro, fact não é a função mais adequada para processar o erro. Vamos aqui declarar a situação anómala e deixar quem "sabe" tratar do erro **) then raise (Minha ("valor = "^string_of_int v)) else if v = 0 then a else fact ~a:(a*v) (v-1) ;; (**objectivo= ler inteiros dum ficheiro, coloca-los numa lista e calcular para todos eles o factorial. Esta função é a função mais adequada para tratar dos erros de argumentos que podem surgir (porque é essa função que extraiu os valores fornecidos a função fact)**) let leitura name = let fich = open_in name in let sb = Scanf.Scanning.from_channel fich in let l = ref [] in try while (true) do let x = Scanf.bscanf sb " %d" (fun a -> a) in l:= x::!l done; [] with End_of_file -> try List.map fact (List.rev !l) with Minha st ->( prerr_string ("Problema com dados invalidos: "^st^"\n");[]) ;; (*** exception Invalid_argument of string -- Exception raised by library functions to signal that the given arguments do not make sense. exception Failure of string -- Exception raised by library functions to signal that they are undefined on the given arguments. exception Not_found -- Exception raised by search functions when the desired object could not be found. exception Out_of_memory -- Exception raised by the garbage collector when there is insufficient memory to complete the computation. exception Stack_overflow -- Exception raised by the bytecode interpreter when the evaluation stack reaches its maximal size. This often indicates infinite or excessively deep recursion in the user's program. (Not fully implemented by the native-code compiler; see section 11.5.) exception Sys_error of string -- Exception raised by the input/output functions to report an operating system error. exception End_of_file -- Exception raised by input functions to signal that the end of file has been reached. exception Division_by_zero -- Exception raised by division and remainder operations when their second argument is null. val raise : exn -> 'a -- Raise the given exception value val invalid_arg : string -> 'a -- Raise exception Invalid_argument with the given string. val failwith : string -> 'a -- Raise exception Failure with the given string. val exit : int -> 'a -- Terminate the process, returning the given status code to the operating system: usually 0 to indicate no errors, and a small positive integer to indicate failure. All open output channels are flushed with flush_all. An implicit exit 0 is performed each time a program terminates normally. An implicit exit 2 is performed if the program terminates early because of an uncaught exception. val at_exit : (unit -> unit) -> unit -- Register the given function to be called at program termination time. The functions registered with at_exit will be called when the program executes exit, or terminates, either normally or because of an uncaught exception. The functions are called in ``last in, first out'' order: the function most recently added with at_exit is called first. http://caml.inria.fr/pub/docs/manual-ocaml/libref/Printexc.html Module Printexc module Printexc: sig .. end Facilities for printing exceptions. val to_string : exn -> string Printexc.to_string e returns a string representation of the exception e. val print : ('a -> 'b) -> 'a -> 'b Printexc.print fn x applies fn to x and returns the result. If the evaluation of fn x raises any exception, the name of the exception is printed on standard error output, and the exception is raised again. The typical use is to catch and report exceptions that escape a function application. ***) (**** Ficheiro e canais ****) (* O tipo dos ficheiros em OCaml designa-se por "canais", ou seja channel. De modo a fornecer uma abstracção cómoda e facilitar o processamento dos dados neles contidos, vário subtipos de canais são disponibilizadas de raíz no OCaml Canais de entrada de texto, canais de saída de texto, etc..., Nesta aula só precisaremos de dois destes tipos de canais: type in_channel -- The type of input channel. type out_channel -- The type of output channel. val stdin : in_channel -- The standard input for the process. val stdout : out_channel -- The standard output for the process. val stderr : out_channel -- The standard error output for the process. val open_out : string -> out_channel -- Open the named file for writing, and return a new output channel on that file, positionned at the beginning of the file. The file is truncated to zero length if it already exists. It is created if it does not already exists. Raise Sys_error if the file could not be opened. val open_out_bin : string -> out_channel -- Same as open_out, but the file is opened in binary mode, so that no translation takes place during writes. On operating systems that do not distinguish between text mode and binary mode, this function behaves like open_out. val flush : out_channel -> unit -- Flush the buffer associated with the given output channel, performing all pending writes on that channel. Interactive programs must be careful about flushing standard output and standard error at the right time. val flush_all : unit -> unit -- Flush all open output channels; ignore errors. val output_char : out_channel -> char -> unit -- Write the character on the given output channel. val output_string : out_channel -> string -> unit -- Write the string on the given output channel. val close_out : out_channel -> unit -- Close the given channel, flushing all buffered write operations. Output functions raise a Sys_error exception when they are applied to a closed output channel, except close_out and flush, which do nothing when applied to an already closed channel. Note that close_out may raise Sys_error if the operating system signals an error when flushing or closing. val open_in : string -> in_channel -- Open the named file for reading, and return a new input channel on that file, positionned at the beginning of the file. Raise Sys_error if the file could not be opened. val open_in_bin : string -> in_channel -- Same as open_in, but the file is opened in binary mode, so that no translation takes place during reads. On operating systems that do not distinguish between text mode and binary mode, this function behaves like open_in. val input_char : in_channel -> char -- Read one character from the given input channel. Raise End_of_file if there are no more characters to read. val input_line : in_channel -> string -- Read characters from the given input channel, until a newline character is encountered. Return the string of all characters read, without the newline character at the end. Raise End_of_file if the end of the file is reached at the beginning of line. val input : in_channel -> string -> int -> int -> int -- input ic buf pos len reads up to len characters from the given channel ic, storing them in string buf, starting at character number pos. It returns the actual number of characters read, between 0 and len (inclusive). A return value of 0 means that the end of file was reached. A return value between 0 and len exclusive means that not all requested len characters were read, either because no more characters were available at that time, or because the implementation found it convenient to do a partial read; input must be called again to read the remaining characters, if desired. (See also really_input for reading exactly len characters.) Exception Invalid_argument "input" is raised if pos and len do not designate a valid substring of buf. val close_in : in_channel -> unit -- Close the given channel. Input functions raise a Sys_error exception when they are applied to a closed input channel, except close_in, which does nothing when applied to an already closed channel. Note that close_in may raise Sys_error if the operating system signals an error. exception End_of_file -- Exception raised by input functions to signal that the end of file has been reached. *) let process () = let iname = print_endline "Nome de ficheiro de input"; read_line() in let oname = print_endline "Nome de ficheiro de output"; read_line() in let rec leitura fich acc = begin let n = int_of_string (input_line fich) in try leitura fich (n+acc) with End_of_file -> acc end in if Sys.file_exists iname && Sys.file_exists oname then let ic = open_in iname in let oc = open_out oname in let v = leitura ic 0 in begin output_string oc (string_of_int v); close_in ic; close_out oc end else failwith "File error";; process ();; (***O tipo "Produto Cartesiano" ***) (* pratico quando queremos devolver mais do que um valor:*) let rec menor_maior l = match l with [] -> invalid_arg "Com listas vazias é que não!!!!!" [el] -> (el,el) el::li -> let (a,b) = menor_maior li in (if a>el then el else a ),(if b<el then el else b) (** alternativa (válida só para pares) ---> let x = menor_maior li in (if fst x>el then el else fst x ),(if snd x<el then el else snd x) **) (** e pode-se com 3? claro!**) let rec menor_maior_soma l = match l with [] -> invalid_arg "Com listas vazias é que não!!!!!" [el] -> (el,el,el) el::li -> let (a,b,s) = menor_maior_soma li in ((if a>el then el else a ),(if b<el then el else b), s+el) ;; (* exercício: definir a função menor_maior_soma_numerodepares_numerodeimpares *) type 'a tipo_fun = ('a * ('a -> 'a));; let f x = x + 1;; (0,f);; type tipo_das_funcoes_inteiras_com_um_elemento = int tipo_fun;; (** sintaxe generica para definição de tipos type ('a1, ... ,'an) name = typedef ;; **) type ('a,'b) fun_type = ('a * ('b -> 'a));; (*** Tipos indutivos, ou tipos soma ***) (******************************************) (* Um dos mecanismos disponíveis no OCaml para definir tipos estruturados é o mecanismo dos tipos "soma" ou igualmente designados de "tipos indutivos" De uma forma informal um elemento dum tipo indutivos define-se pela forma que pode ter. Por exemplo: um dia pode ter 7 formas. ou é segunda feira, ou é terça feira etc.... Uma propriedade deste tipo de elemento (e de definição de conjunto) é que um elemento tem uma forma *única*. No exemplo, é simples.... um dia não pode ser segunda feira e simultaneamente terça feira. Um dos exemplo clássicos é o tipo das listas: uma lista é, de forma exclusiva, ou a lista vazia ou uma lista que tem um elemento a cabeça e uma outra lista em cauda. Esta descrição é suficiente para descrever todas as listas possíveis. Obviamente, estes mecanismo suporta o polimorfismo e a ordem superior. Em termos de sintaxe, como é? Cada caso na definição das formas que um elemento do tipo pode ter, tem um nome, designado de *constructor*. Este nome é um caso particular duma função e começa sempre por uma maiúscula, e pode opcionalmente conter parâmetros. Um constructor quando devidamente parametrizado constrói um elemento do tipo. Por exemplo, quando devidamente instanciado por 0 e [1;2;3], o constructor :: constrói uma lista com 0 a cabeça e [1;2;3] a cauda: [0;1;2;3] Vejamos exemplos (e a sintaxe....) *) type dia = Seg Ter Qua Qui Sex Sab Dom;; type 'a minha_lista = LVazia Junta of 'a * ('a minha_lista) (* O tipo minha_lista tem dois tipos de elementos: a lista vazia (constructor LVazia, sem parâmetros) ou a lista com pelo menos um elemento (constructor Junta, com dois parâmetros, o elemento e a lista a qual se junta este elemento) *) (* um elemento: *) let umalista = Junta (0, Junta (1,(Junta (2,LVazia)))) let umaoutralista = Junta ('a', Junta ('b',(Junta ('c',(Junta ('d',LVazia)))))) let mais_uma = Junta (-4 , umalista) (** Repare na utilização de match **) let rec comprimento ml = match ml with LVazia -> 0 Junta (a,b) -> 1 + (comprimento b);; comprimento umalista;; comprimento umaoutralista;; let rec concatena ml1 ml2 = match ml1 with LVazia -> ml2 Junta (a,b) -> Junta (a,(concatena b ml2)) type figurageometrica = Triangulo of float*float Rectangulo of float*float Quadrado of float let definir () = let resp = print_string "Que figura? (0 -> T, 1 -> R, ... -> Q)\n" ; read_int () in if resp = 0 then (print_string "altura base: \n"; scanf " %f %f" (fun a b -> Triangulo (a,b))) else if resp = 1 then (print_string "largura comprimento: \n"; scanf " %f %f" (fun a b -> Rectangulo (a,b))) else (print_string "lado: \n"; scanf " %f" (fun a -> Quadrado a)) let area fg = match fg with Triangulo (a,b) -> (a *. b )/. 2. Rectangulo (a,b) -> a *. b Quadrado a -> a *. a type 'a ab = Vazia Nodo of ('a ab)*'a*('a ab) let exemplo = Nodo ((Nodo ((Nodo (Vazia,1,(Nodo (Vazia,2,Vazia)))),3,(Nodo (Vazia,4,Vazia)))),5,(Nodo (Vazia,7,Vazia)));; let max a b = if a > b then a else b;; let rec altura ar = match ar with Vazia -> 0 Nodo (Vazia,a,Vazia) -> 0 Nodo (e,a,d) -> 1 + max (altura e) (altura d) ;; let rec dfs ar = match ar with Vazia -> [] Nodo (e,a,d) -> (dfs e)@ [a]@(dfs d) ;; let rec map_ab f ar = match ar with Vazia -> Vazia Nodo (e,a,d) -> Nodo (map_ab f e, f a, map_ab f d) ;; (* Vamos definir o tipo das cartas onde os valores não são inteiros *) type valor = Dois Tres Quatro Cinco Seis Sete Oito Nove Dez Valete Rainha Rei As type carta = Ouros of valor Paus of valor Espadas of valor Copas of valor (* vamos assumir Ouros < Paus < Espadas < Copas *) let ganha_valor v1 v2 = match (v1,v2) with As,As -> true As, _ -> false Dois,As Dois,Dois -> true Dois, _ -> false Tres, As Tres, Dois Tres, Tres -> true Tres, _ -> false Quatro, As Quatro, Dois Quatro, Tres Quatro, Quatro -> true Quatro, _ -> false Cinco, As Cinco, Dois Cinco, Tres Cinco, Quatro Cinco, Cinco -> true Cinco, _ -> false Seis, As Seis ,Dois Seis, Tres Seis, Quatro Seis, Cinco Seis,Seis -> true Seis, _ -> false Sete, As Sete ,Dois Sete, Tres Sete, Quatro Sete, Cinco Sete,Seis Sete, Sete -> true Sete, _ -> false Oito, As Oito ,Dois Oito, Tres Oito, Quatro Oito, Cinco Oito,Seis Oito, Sete Oito, Oito -> true Oito , _ -> false Nove, As Nove ,Dois Nove, Tres Nove, Quatro Nove, Cinco Nove,Seis Nove, Sete Nove, Oito Nove, Nove -> true Nove , _ -> false Dez, As Dez ,Dois Dez, Tres Dez, Quatro Dez, Cinco Dez,Seis Dez, Sete Dez, Oito Dez, Nove Dez, Dez -> true Dez , _ -> false Valete, As Valete ,Dois Valete, Tres Valete, Quatro Valete, Cinco Valete,Seis Valete, Sete Valete, Oito Valete, Nove Valete, Dez Valete, Valete -> true Valete , _ -> false Rainha, As Rainha ,Dois Rainha, Tres Rainha, Quatro Rainha, Cinco Rainha,Seis Rainha, Sete Rainha, Oito Rainha, Nove Rainha, Dez Rainha, Valete Rainha, Rainha -> true Rainha , _ -> false Rei, As Rei ,Dois Rei, Tres Rei, Quatro Rei, Cinco Rei,Seis Rei, _ -> true (* mais simplesmente podemos usar o atalho seguinte se assumirmos que Dois < Tres < Quatro < Cinco < Seis < Sete < Oito < Nove < Dez < Valete < Rainha < Rei < As *) let ganha_valor v1 v2 = v1 >= v2 let ganha c1 c2 = match (c1,c2) with Ouros v1,Ouros v2 -> ganha_valor v1 v2 Ouros _, _ -> false Paus _,Ouros _ -> true Paus v1,Paus v2 -> ganha_valor v1 v2 Paus _, _ -> false Espadas _ , Ouros _ Espadas _ , Paus _ -> true Espadas v1,Espadas v2 -> ganha_valor v1 v2 Espadas _, _ -> false Copas v1,Copas v2 -> ganha_valor v1 v2 Copas _, _ -> true (** Vamos agora explorar de forma informal tipos de dados estruturados clássicos em informática: árvores e grafos Comecemos com arvores binárias. Uma árvore binária ou é uma folha ou é um nodo com um filho esquerdo e um filho direito. **) (* árvore binária de Inteiros *) type intbintree = Vazia Nodo of intbintree * int * intbintree (* agora com polimorfismo... árvores binárias de elementos de "qualquer tipo" *) type 'a bintree = Leaf Node of 'a bintree * 'a * 'a bintree (* assim as árvores binarias de inteiros são vistas como uma instância do tipo anterior *) type int_bintree = int bintree (* Para generalizar, as árvores *) type 'a tree = TLeaf TNode of 'a * ('a tree) list let a1 = Node (Node (Leaf,0,Leaf),6, (Node (Leaf,9,Leaf))) (* inserção ordenada *) let rec insere x ar = match ar with Leaf -> Node (Leaf,x,Leaf) Node (e,a,d) -> if x>a then Node (e,a,(insere x d)) else Node(insere x e, a, d) let rec soma ar = match ar with Leaf -> 0 Node (e,a,d) -> a + (soma e) + (soma d) let rec percurso ar = match ar with Leaf -> [] Node (e,a,d) -> (percurso e) @ (a::(percurso d)) let rec altura ar = match ar with Leaf -> 0 Node (e,a,d) -> let v,w = (altura e), (altura d) in (if v > w then v+1 else w+1) ;; soma a1;; altura a1;; percurso a1;; (** e para as árvores gerais **) let rec somag ar = match ar with TLeaf -> 0 TNode (a,f) -> a + List.fold_left (fun acc el -> somag el + acc) 0 f let rec percursog ar = match ar with TLeaf -> [] TNode (a,f) -> a::(List.fold_left (fun acc el -> (percursog el)@acc) [] f) let rec alturag ar = match ar with TLeaf -> 0 TNode (a,f) -> 1 + (List.fold_left (fun acc el -> let v = (alturag el) in if v > acc then v else acc ) 0 f) (* Vamos agora explorar uma outra estrutura de dados clássica em informática: os grafos. Há várias formas simples de os representar (vamos nos limitar aqui ao "obvio"): matriz de adjacências, listas de adjacências directas Um grafo tem nodos e vertices, pode ser dirigido e ter conteúdos nos nodos ou nos vertices. *) (* cada nodo é indexado por un inteiro (o seu número) 'a --> tipo da informação nos nodos 'b --> tipo da informação nos vertices (int*'b) --> o numero do nodo destino e a informação que o vertice contém (int * 'a) --> o nodo de numero int tem por informação 'a (int*'b) array array--> a matriz de adjacência em si. (int * 'a) list ---> a listagem dos nodos com os seus conteúdos *) type ('a,'b) grafogm = (int*'b) array array * (int * 'a) list (* bom, ficando-nos no caso mais simple... *) type grafo = bool array array (* ou ainda, via lista de adjacência *) type grafol = (int * (int list)) list (** Algumas funções sobre grafos, vistos como listas de adjacências **) (* qual é a lista dos nodos vizinhos dum nodo i? *) let nextl (g:grafol) (i:int) = List.assoc i g;; (* fundir duas listas de valores sem causar repetições *) let merge l1 l2 = List.fold_left (fun acc e -> if List.mem e acc then acc else e::acc) l1 l2 (* Dado um grafo g, que nodos é possível atingir a partir de uma lista de nodos l (prerequisitos l está ordenada e sem repetição de elementos) ? *) let rec atingiveis (g: grafol) (l: int list) = (*** vizinhos = lista ordenada (sem repetição, graças a função merge) dos vizinhos dos nodos presentes em l***) let vizinhos = List.sort compare (fold_left (fun acc a -> merge (nextl g a) acc) [] l) in (*** se vizinhos = l então nada de novo aconteceu... então de certeza que nada de novo acontecerá -> caso de paragem***) if vizinhos = l then l else atingiveis g (sort compare (merge l vizinhos) ) (*** um exemplo simples ***) let umgrafo = [(1,[1;2;3;4;5]);(2,[1;3]);(3,[1;2]);(4,[1;5;6;7]);(5,[1;4;6]);(6,[4;5]);(7,[4]);(8,[9]);(9,[8])];; nextl umgrafo 1;; nextl umgrafo 8;; atingiveis umgrafo [8];; atingiveis umgrafo [1];; atingiveis umgrafo [1;8];; (*** e com grafos de tipo grafo (matriz de adjacência) ? ***) (**** algumas funções que vamos usar.... val iter : ('a -> unit) -> 'a array -> unit -- Array.iter f a applies function f in turn to all the elements of a. It is equivalent to f a.(0); f a.(1); ...; f a.(Array.length a - 1); (). val iteri : (int -> 'a -> unit) -> 'a array -> unit -- Same as Array.iter, but the function is applied to the index of the element as first argument, and the element itself as second argument. ****) let next (g:grafo) i = let l = ref [] in let _= Array.iteri (fun j v -> if v then l:=j::!l) g.(i) in !l;; (* Dado um grafo g que nodos é possível atingir a partir de uma lista de nodos l ? *) let rec atingiveisg (g: grafo) (l: int list) = let vizinhos = sort compare (fold_left (fun acc a -> merge (next g a) acc) [] l) in if vizinhos = l then l else atingiveisg g (sort compare (merge l vizinhos) ) let maisumgrafo = [| [| true ; true ; true ; true ; true ; false ; false ; false ; false |]; [| true ; false ; true ; false ; false ; false ; false ; false ; false |]; [| true ; true ; false ; false ; false ; false ; false ; false ; false |]; [| true ; false ; false ; false ; true ; true ; true ; false ; false |]; [| true ; false ; false ; true ; false ; true ; false ; false ; false |]; [| false ; false ; false ; true ; true ; false ; false ; false ; false |]; [| false ; false ; false ; true ; false ; false ; false ; false ; false |]; [| false ; false ; false ; false ; false ; false ; false ; false ; true |]; [| false ; false ; false ; false ; false ; false ; false ; true ; false |]; |] ;; (*******************************************) next maisumgrafo 7;; next maisumgrafo 0;; atingiveisg maisumgrafo [7];; atingiveisg maisumgrafo [0];; atingiveisg maisumgrafo [0;7];; (**** um exemplo completo com o tipo das expressões aritméticas de base. Vamos implementar uma derivadora básica. ****) open Scanf open List open Stream open Genlex open Printf open Str (* Universo U = (Var União N União {+,-,*,\,(,)})* expression é o menor X subconjunto de U verificando (B) e (I) (B) 2 casos a) qualquer que seja o inteiro i, i pertence ao conjunto X b) qualquer que seja a variável v, v pertence ao conjunto X (I) 4 passos indutivos 1) qualquer que sejam f e g elementos de X, então (f + g) pertence a X 2) qualquer que sejam f e g elementos de X, então (f - g) pertence a X 3) qualquer que sejam f e g elementos de X, então (f * g) pertence a X 4) qualquer que sejam f e g elementos de X, então (f / g) pertence a X *) type variavel = string type expression = Const of int (* i *) Var of variavel (* x *) Sum of expression * expression (* e1 + e2 *) Diff of expression * expression (* e1 - e2 *) Prod of expression * expression (* e1 * e2 *) Divi of expression * expression (* e1 / e2 *) (* Funções de leitura que permitam ler do teclado expressões do genero "(a + 5) * 2 / ( b - 4)" devolver um elemento de tipo expression que representa a expressão lida Divi ((Prod (Sum (Var "a", Const 5),( Const 2))),(Diff ((Var "b"),(Const 4)))) Para tal é necessario fazer o que chamamos um "parser" capaz de ler uma string e reconhecer a expressão que nela está contida. Veremos no segundo semestre estas técnicas de forma pormenorizada, aqui vamos nos contentar em usar uma receita "já feita" disponível no OCaml. Numa primeira leitura, podem omitir a compreensão da construção do parser. Aqui vai uma gramática LL(1) que serve de base ao parser "caseiro" E ::= T E' E' ::= + T E' E' ::= - T E' E' ::= \epsilon T ::= F T' T' ::= * F T' T' ::= / F T' T' ::= \epsilon F ::= N F ::= V F ::= ( E ) *) let lexer = make_lexer ["("; ")"; "+"; "*"; "-"; "/"];; let rec parse_expr = parser (* corresponde a entrada E da gramatica *) [< e1 = parse_mult; e = parse_more_adds e1 >] -> e and parse_more_adds e1 = parser (* corresponde a entrada E' da gramatica *) [< 'Kwd "+"; e2 = parse_mult; e = parse_more_adds (Sum(e1, e2)) >] -> e [< 'Kwd "-"; e2 = parse_mult; e = parse_more_adds (Diff(e1, e2)) >] -> e [< >] -> e1 and parse_mult = parser (* corresponde a entrada T da gramatica *) [< e1 = parse_simple; e = parse_more_mults e1 >] -> e and parse_more_mults e1 = parser (* corresponde a entrada T' da gramatica *) [< 'Kwd "*"; e2 = parse_simple; e = parse_more_mults (Prod(e1, e2)) >] -> e [< 'Kwd "/"; e2 = parse_simple; e = parse_more_mults (Divi(e1, e2)) >] -> e [< >] -> e1 and parse_simple = parser (* corresponde a entrada F da gramatica *) [< 'Ident s >] -> Var s [< 'Int i >] -> Const i [< 'Float f >] -> Const (int_of_float f) [< 'Kwd "("; e = parse_expr; 'Kwd ")" >] -> e;; let parse_expression = parser [< e = parse_expr; _ = Stream.empty >] -> e;; (* função principal de leitura usando streams*) let expr_of_string s = parse_expression(lexer(Stream.of_string s));; let read_expression () = expr_of_string (read_line ());; let rec do_n_times f n acc = if n<=0 then rev acc else let v= f () in (do_n_times f (n-1) (v::acc)) (****Funções de escrita ****) (* Função (recursiva estrutural) de conversão de expressões para strings *) let rec string_of_expr ex = match ex with Const c -> string_of_int c Var v -> v Sum(f, g) -> ("( "^ string_of_expr f ^ " + " ^ string_of_expr g ^" )") Prod(f, g) -> ("( "^ string_of_expr f ^ " * " ^ string_of_expr g ^" )") Diff(f, g) -> ("( "^ string_of_expr f ^ " - " ^ string_of_expr g ^" )") Divi(f, g) -> ("( "^ string_of_expr f ^ " / " ^ string_of_expr g ^" )") exception Unbound_variable of string;; let rec eval (env: (variavel * int) list) exp = match exp with Const c -> float_of_int c Var v -> ( try float_of_int (List.assoc v env) with Not_found -> raise(Unbound_variable v)) Sum(f, g) -> eval env f +. eval env g Diff(f, g) -> eval env f -. eval env g Prod(f, g) -> eval env f *. eval env g Divi(f, g) -> let valor = eval env g in if valor = 0.0 then failwith ("Divisão por ZERO!!! "^ string_of_expr g) else eval env f /. valor;; let rec deriv ex dv = match ex with Const c -> Const 0 Var v -> if v = dv then Const 1 else Const 0 Sum(f, g) -> Sum(deriv f dv, deriv g dv) Diff(f, g) -> Diff(deriv f dv, deriv g dv) Prod(f, g) -> Sum(Prod(f, deriv g dv), Prod(deriv f dv, g)) Divi(f, g) -> Divi (Diff(Prod(deriv f dv, g), Prod(f, deriv g dv)), Prod(g, g)) ;; let main () = let formula = read_expression () in let v = read_line() in let nvar = read_int () in let env = do_n_times (fun () -> let s = (scanf " %s" (fun a -> a)) in let i = read_int () in (s,i)) nvar [] in let derivada = deriv formula v in let valorf = eval env formula in let valord = eval env derivada in printf "lido:%s\nderivada:%s\nvalor:%f\nvalor derivada:%f\n" (string_of_expr formula) (string_of_expr derivada) valorf valord ;; main ();; (**** Fim do exemplo completo (mas no entanto simplista)****)
This document was generated using caml2html