(******************************************************************************)
(******************************************************************************)
(****                             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"
;;
     




This document was generated using caml2html