Skip to content

Instantly share code, notes, and snippets.

@vik-y
Created May 4, 2016 17:42
Show Gist options
  • Save vik-y/ed62065c9d15717686043116f5897d97 to your computer and use it in GitHub Desktop.
Save vik-y/ed62065c9d15717686043116f5897d97 to your computer and use it in GitHub Desktop.
Ocaml Basics
(*
* This document contains code written to learn Ocaml before the
* Ocaml quiz.
* Let's do this in a hope to learn Ocaml properly.
* Author: Vikas Yadav
* Email: [email protected]
*)
(* Definition of Union/Record Types *)
type number = Integer of int | Rational of int * int ;;
(* Record can also be defined as following *)
type student = { name: string; age: int};;
(* Here initialization of number and student will be different *)
(*
Integer(5);; // Number type
let vikas = {name="vikas"; age="20";};; // Different kind of record
definitions
*)
let a = Rational(5,2);;
let b = Integer(10);;
(* Output of both a and b should be number *)
let add n1 n2 =
match(n1, n2) with
Integer(i1), Integer(i2)-> Integer(i1+i2)
| Rational(m1, d1), Rational(m2,d2) -> Rational(m1*d2 + m2*d1, d1*d2)
| Rational(m1, d1), Integer i -> Rational(m1 + i*d1, d1)
| Integer i, Rational(m1, d1) -> Rational(m1 + i*d1, d1)
(* Adds two numbers and returns a number. Can take both integer or rational number*)
type shape = {
length: int;
breadth: int;
}
let square = {length = 10; breadth = 20;};;
type anothershape = {
length: int;
breadth: int;
}
let testshape = {length = 10; breadth=50};;
let square = {length = 10; breadth = 20;};;
(* The above demo of Shape, and AnotherShape shows us that we can't create two record types with same attribute names
* Even if the name of record types are different. So this concept is not similar to the concept of classes and structures
* in C
*)
(* Understand Tail Recursion in Ocaml *)
(* Gist of it
* One recursion does not depend on another. For example parent won't depend on child at all.
* And hence wont' wait for any data and hence the stack entry for parent can be freed as soon
* as its done.
*)
(* An Example of tail Recursion *)
let rec make_list n = if n = 0 then [] else n :: make_list (n - 1);; (* Without tail recursion it gives error for large values of n *)
let rec make_list n l = if n = 0 then l else make_list (n - 1) (n :: l);; (* Now we made this tail recursive by adding an accumulator *)
let rec tailmap f l a = match l with
| [] -> a
| h :: t -> tailmap f t (f h :: a);; (* Example of map function with tail recursion *)
(* tail map generates an output which is in reversed order
* to correct that we can use another function to reverse the list in a tail recursive way
*)
let rev l =
let rec helper l a = match l with
| [] -> a
| h :: t -> helper t (h :: a)
in
helper l [];;
(*
* Refs and Arrays in Ocaml
* Ocaml doesn't have side effects since it is a functional programming language
* But there are some mutable data structures present in ocaml too which mide lead
* to side effects but are still there to give some ease to the programmers and
* should be used only if absolutely necessary
*)
let x = ref 1;; (* Referencing *)
!x;; (* Dereferencing *)
let f() = !x;; (* function binds with pointer *)
f();; (* This should print 1*)
x:=30;;
f();; (* This should print 30 *)
let f x =
let i = ref x in
while !i> 0 do
Printf.printf "%d\n" !i;
i:=!i-1;
done;;
(* An exampleof programming with side effects. *)
(* If you wanted to do same thing in Ocaml then you would do somethign like this *)
let rec print_all x =
match x with
l -> if l > 0 then (Printf.printf "%d\n" l; print_all (l-1) )else print_int 0;;
print_all 0;
type name = {fn: string; sn: string};;
let sujit = { fn = "sujit"; sn="chak"};;
(* Immutable Record declaration *)
type name = {mutable fn: string; sn: string};;
let sujit = { fn = "sujit"; sn="chak"};;
sujit.fn <- "Vigyan";;
(* Same declaration made mutable *)
let place = "Mill";;
place.[1] <- 'a';; (* We can see here that String is by default a mutable data type *)
place;;
(* Same way arrays are also mutable and can be modified with the same syntax as above *)
List.map (fun x -> x* x) [1;2;3];; (* Example how a function can be directly passed without writing a separate function *)
let add1 x y = x + y ;;
let add2 x = fun y -> x+y;; (* Here 2nd argument to add2 will automatically become y *)
(*
val add2 : int -> int -> int = <fun>
# add1 1 2;;
- : int = 3
# add2 1 2;;
- : int = 3
The way arguments gets passed automatically here, it's called currying style of programming
*)
(* Streams *)
type 'a mystream =
End
| Cons of 'a * (unit -> 'a mystream)
let hd = function
End -> failwith "emtpy"
| Cons(h,_) -> h
let tl = function
End -> failwith "emtpy"
| Cons(_,t) -> t
let string_stream s =
let rec iter n =
if n >= String.length s then End
else Cons(s.[n], fun()-> iter(n+1))
in
iter 0;
;;
let rec ns n = Cons(n,fun() -> ns (n + 1))
(* hd of object gives the number
* Tl of object points to the next object.
Please note that tl stream will just return you a function.
You have to run that function to take another stream.
E.g.
let temp = string_stream "Hey this is vikas";;
let t = tl temp;;
Note that t is right now a function.
Execute t();; and this will return you a mystream object.
Now if you do hd(t());; then this will give you the next character in the stream.
type state =
Terminate of bool
| State of (char mystream -> state);;
(* Some Generic questions which might be useful for tomorrow's exam
* Difference between mutablea and immutable data types
*)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment