(******************************************************************************) (******************************************************************************) (**** Guião 1 ***) (**** expressões, variáveis, declarações, tipos, funções, recursividade ***) (**** ***) (******************************************************************************) (******************************************************************************) (**** versão documento (html, pdf) : utilizar ocamlweb ou ocamldoc ***) (* para usar preferencialmente no toplevel por omissão ou com utop *) open Printf;; (*s Para começar, os comentários são textos colocados entre (* e *). Podem ficar aninhados. Estes são, sem surpresa ignorados pelo compilador, mas podem ser aproveitados - para além dos programadores, por ferramentas de geração de documentação como ocamlweb ou ocamldoc (como este comentário aqui)*) (* Isto é um comentário. (* isto também *) *) (** Em OCaml não há pontos de entrada como em Pascal (com o begin principal e end principal) ou como em C (a função int main()). Um programa OCaml é uma sequência de expressões, de declarações. As expressões podem ser colocadas entre parêntesis ou begin end, ou por ;; finais (esta ultima opção é obrigatória no toplevel) : ( expressão) begin expressão end let _ = expressão expressão;; (* com dois pontos e vírgulas *) Para ser mais prático e nesta fase de iniciação iremos sempre colocar ;; Mas as boas práticas da programação OCaml (fora do toplevel) aponta para a versão: let _ = expressão **) 7+9*3;; "ola " ^ "tudo bem";; [6;4;5]@[7;9];; sin 4.6;; List.rev ([6;4;5]@[7;9]);; List.rev [6;4;5]@[7;9];; (** uma declaração é definida por let identificador = expressão uma expressão OCaml é uma expressão que devolve um valor (OCaml é uma linguagem fortemente tipada, logo tudo tem um tipo) LEMA: um programa OCaml é uma sequência de expressões uma expressão tem sempre um e um só valor uma expressão tem sempre um e um só tipo **) let x = 5 ;; let y = 4 + 7 * 6;; (*** As variáveis OCaml são, por omissão, como em matemática. É uma associação única ente identificador e valor. Uma variável tem um único valor (este não pode mudar). o que acontece então no exemplo seguinte? ***) let x = 5;; let z = x +12;; x;; z;; let x = 10;; x;; z;; let y = 3 + x;; y;; print_int y;; (*** Estes exemplos ilustram como várias variáveis de mesmo nome co-existam e se escondam umas as outras. A última tem sempre prioridade, isto é, adquire a visibilidade e as outras com nome conflituoso ficam ofuscadas. Mais. Se nenhum valor depende duma determinada variável, digamos x, e esta está escondida por outras variáveis, então x esta definitivamente inacessível e "inútil". o recuperador de memória (Garbage Collector) libertará automaticamente o espaço ocupado por x. ***) (** Alguns exemplos simples **) (** Hello World! numa simples linha**) print_string "Hello World!\n";; print_endline "Hello world";; print_int 5 ; print_newline ();; print_float 5.5;; printf "%d %f" 6 6.8;; (*Sem o [open Printf;;] inicial poderia chamar a função da seguinte forma: Printf.printf "Ola %d\n" 7 *) (*** OCaml é fortemente tipado. As conversões entre tipos devem ser explicitamente colocadas pelo programador ***) (** a seguinte expressão dá erro. Porquê? 1 + 9.0;; **) (* a seguinte expressão dá erro. Porquê? 1 + (sin 0.5);; Como corrigir? assim : *) 1 + int_of_float (sin 0.5);; sin (float_of_int 1);; float_of_int 1;; sin 1.;; (** As funções são valores como os outros (voltaremos a este assunto...) Consegue perceber o resultado? **) 5;; (+);; sin;; (** Cuidado, como em muitas linguagens (como em C), a aritmética de flutuantes em OCaml, normalizada pelo IEEE 754, é uma aritmética com aproximações. Surpresas podem advir deste facto... Ver por exemplo: http://floating-point-gui.de/errors/comparison/ https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html **) 0.15 +. 0.15 = 0.1 +. 0.2;; let a = 0.15 +. 0.15 ;; let b = 0.1 +. 0.2 ;; a=b;; (*** OCaml é uma linguagem fortemente tipada, mas o tipo não precisa de ser fornecido: o OCaml consegue *inferir* o tipo de cada expressão ***) let x = sin 0.17;; (**Caso se pretenda tornar explicito o tipo (ou forçar uma tipagem particular)**) let (x:int) = int_of_float (sin 0.17);; let v = (int_of_float (sin ((float_of_int 6) +. 7.0)));; (* Para perceber melhor como OCaml infere os tipos vejamos como consegue calcular o tipo da função aqui declarada (falaremos já a seguir e mais em detalhe dos mecanismos de definição de função). [u] é a função que a [x] associa [x + 1]. Como já vimos, o simbolo + é uma função binária de tipo int -> int -> int (dados dois inteiros, devolve um terceiro inteiro, a soma deles). Assim a função u devolve x + 1 por isso, para haver tipagem certa, 1 e x devem ser inteiros. Se esta condição é obviamente verificada por 1, esta deve ser imposta a x. Assim x tem de ser inteiro. Logo u é a função de tipo int -> int *) let u x = x + 1;; u 5;; (* qual é o tipo de x? de y? e, finalmente, de v? *) let v x y = sin x +. float_of_int (u y);; v 0. 9;; (*** Declarações locais ====> let identificador = expressão1 in expressão2 O identificador assim definido tem por valor expressão1 e por alcance (i.e. é valido em) expressão2. Fora desta expressão2, identificador já não existe. ***) let x = 2;; let x = 9 in x*x;; x;; let x = 10;; (* as declarações podem ser aninhadas *) let x = 9 in let y = 10 in let t = 2 in (x+y+t)*(x-y-t);; x;; (* De mais fácil leitura *) let x = 9 in let y = 10 + x in let t = 2 in (x+y+t)*(x-y-t);; (*Dá erro. Porquê? print_int t;; *) (* ou ainda - Dá erro. Porquê? let x = 1 in (let t = 2 +x in let y = t in let x = 8 in x + y) * (let z = 1 in let y = 7 in x * (z+y+t));; *) (*** condicional funcional: a expressão if then else não foge a regra das outras expressões: tem de devolver um valor. O valor devolvido no then tem de ser do mesmo tipo que o valor devolvido no else. ***) let x = 6;; let v = if x > 7 then 9 else 1;; if v > 3 then print_int 5 else print_int 6;; let w = 1 + (let u = x + 4 in if x = 0 then u+2 else 3) (*** Funções são valores como outros. Definição simples duma função: let nome lista_de_parametros = expressão se a função for recursiva, utiliza-se a declaração: let rec nome lista_de_parametros = expressão **) let f (x:int) (y:int) (z:int) = x + y + z;; f 3 4 5;; let f x y z = x + y + z;; (* a condicional aliada às funções recursivas permitam as construções repetitivas Vejamos os clássicos*) let rec fib n = if n < 2 then 1 else fib (n-1) + fib (n-2);; let rec fact n = if n <= 0 then failwith "Espero valores positivos!" else if n=1 then 1 else n * fact (n-1);; (** failwith "x" : Abortar a execução com a mensagem "x" (ver lição sobre excepções) **) (** As funções OCaml permitam avaliação parcial **) let pppp = (f 3 4);; pppp 7;; ((f 4) 5 6);; let h = f 4;; let h y z = f 4 y z;; h 5 6;; f 4;; let u y x z = f x y z;; u 5;; let operacao x y f = f x y;; let misterio = operacao 4 8;; misterio (+);; operacao 4 8 (+);; operacao 4.9 8.1 (+.);; let somalist x y = List.length x + List.length y;; operacao [1;2;3;4] [4;5;6;7;8;9] somalist;; operacao [1;2;3;4] [4;5;6;7;8;9] (fun x y -> List.length x + List.length y);; let h x y = 2 * x + y;; operacao 4 5 h;; let g x y z = x +. y +. z;; let h x y = (int_of_float x) + y;; (*Dá erro. Porquê? let h' (x:int) y = (int_of_float x) + y *) h 6.0 9;; (* uma brincadeira: como definir uma função binária f e usa-la de forma infixa? basta declara-la com parêntesis *) let (>>) x y = 2 * x + 3 * y;; 4 >> 8;; (>>) 4 8;; let mais_dois x = x + 2;; let rec soma_natural x y = if x < 0 || y < 0 then failwith "Parametros negativos" else if x = 0 then y else (soma_natural (x-1) (y+1)) ;; mais_dois 8;; soma_natural 5 7;; print_int (soma_natural 7 7);; print_string Sys.os_type;; print_newline();; print_string (Sys.getcwd ());; print_string (Sys.argv.(0));; print_newline();; print_string Sys.argv.(0);; open Sys;; (**** variações simples a volta da função fib. ****) exception Input_Negativo;; (* versão recursiva simples (não recursiva terminal)*) let rec fib1 x = if (x<0) then raise Input_Negativo else if (x <= 1) then 1 else fib1 (x-1) + fib1 (x-2) ;; fib1 5;; fib1 (-8);; let ht = Hashtbl.create 100;; let rec fibt n = try Hashtbl.find ht n with Not_found -> (if n <= 1 then ((Hashtbl.add ht n 1); 1) else let valor = fibt (n-1) + fibt (n-2) in ((Hashtbl.add ht n valor);valor) );; let x = read_int () in try fib1 x with Input_Negativo -> fib1 (- x);; (* versão terminal recursiva *) let rec fib_fast x acc1 acc2 = if x<0 then raise Input_Negativo else if x = 0 then acc1 else if x = 1 then acc2 else fib_fast (x-1) acc2 (acc2+acc1) ;; let fib2 n = fib_fast n 1 1 ;; (* calcular fibonacci a partir de várias fontes *) let calcula () = (* recuperar o argv e o seu comprimento (argv é um array de strings)*) let narg = Array.length Sys.argv in (* se o # de argumentos é 1, então ler do stdin*) if narg = 1 then begin print_string "Queira introduzir um valor inteiro: "; let x = read_int() in fib2 x end else (* se for 2 então o segundo argumento tem de ser um inteiro (lança uma excepção de tipo " invalid cast" caso negativo)*) if narg = 2 then begin fib2 (int_of_string Sys.argv.(1)) end else if narg>3 then failwith "Bad Use\n" else (* se for igual a 3 e que o segundo é -f então o terceiro é o nome do ficheiro por abrir*) if ((Sys.argv.(1) = "-f") && (Sys.file_exists (Sys.argv.(2)))) then begin let fich= open_in Sys.argv.(2) in fib2 (int_of_string (input_line fich)) end else failwith "Bad Use\n" ;;