02-exercises

02-basic_types

Types

In OCaml there are 6 basic types: [int], [float], [char], [string], [bool],

and [unit].

1
2
3
4
5
6
7
8
OCaml type  Range

int 63-bit signed int on 64-bit processors, or 31-bit signed int on
32-bit processors
float IEEE double-precision floating point, equivalent to C's double
bool A boolean, written either 'true' or 'false'
char An 8-bit character
string A string (sequence of 8 bit chars)

string

String defination and explanation as follows:

1
2
3
4
5
6
7
8
9
10
(* define a string parameter with name first_name *)
let first_name = "Fred"
(* define a string paramter with name last_name, and type is string.
important s is a lower case letter
'name: type =' equals to 'name =' *)
let last_name : string = "Flintstone"
(* String concatenation is done with the [( ^ )] operator.
between two " " has a space. The output for this is
val full_name : string = "Fred Flintstone" *)
let full_name = first_name ^ " " ^ last_name

Unit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
(** ========== Booleans ========== **)                
let a_boolean_false : bool = false

(* You can use
[( && )] for logical and
[( || )] for logical or

What does the following do? *)
let () = assert (true || a_boolean_false)
(** ========== The [unit] Type ========== **)
(* [unit] is a special type in OCaml that has only one possible value, written
(). It is generally used for mutation and I/O-operations.

(I/O stands for input/output. Examples: printing to screen, reading a file,
sending and receiving network requests.) *)

let () = Stdio.print_endline "Hi, my name is Fred Flintstone and I am 5 years old"

(* To combine several unit operations together, the [;] operator is used. *)
let () =
Stdio.print_endline "Hi, my name is ";
Stdio.print_endline full_name;
Stdio.print_endline " and I am 5 years old"
(* When evaluating a unit operation on the toplevel, you should wrap it in a
[let] binding, as in

{| let () = ... |}

This isn't actually necessary in all cases, but it will save you from some
frustrating debugging of compiler issues if you just always include it.

For this reason, idiomatic OCaml code has [let () = ...] as its entrypoint,
similar to the [main] function in languages like C, C++ and Java (although in
those languages it is required). *)

(* An aside on printing: Like many other programming languages, you can use
formatted strings for printing! We also use the '\n' character for printing
newlines. *)
let () =
Stdio.printf "Hi, my name is %s and I am %d years old\n" full_name 5

Inline Test - todo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(** ========== Inline Tests ========== *)
(* The lines that follow are inline tests. Each evaluates a boolean expression.
They are run during the build, and failures -- evaluating to false -- are
treated like compile errors by the build tool and editors.

We will see other kinds of inline tests later, and some interesting patterns
for using them. *)

(* An aside on modules and comparison: While OCaml supports polymorphic
comparison (i.e. comparison of values of arbitrary types), it is good
practice to use equality and comparison functions specific to each type.

Types, and functions that operate on those types, are gathered into
namespaces that we call "modules". We can access functions inside modules
using the syntax [Module.function].

So, [Int.equal] is the [equal] function defined in the [Int] module. Its
signature is

{| val equal : int -> int -> bool |}

In words: [equal] takes two [int]s and returns a [bool]. The following line
is applying that function to two inputs, [5] and [int_average 5 5]. *)

let%test "Testing int_average..." =
Int.equal (int_average 5 5) 5

(* Modules can also contain operators. By convention, the equality operator is
defined and equivalent to the [equal] function. To reference an operator in a
module, we need to surround it with parentheses.

Try removing the parentheses around the "=" and compiling to see what error
you get.

[Int.(=)] is the same as [Int.equal]. *)


let%test "Testing float_average..." =
Float.(=) (float_average 5. 5.) 5.

let%test "Testing float_average..." =
Float.equal (float_average 5. 10.) 7.5

Patttern Match

Pattern Matching

Instead of using one or more if … then … else … constructs to make choices in OCaml programs, we can use the match keyword to match on multiple possible values. Consider the factorial function:

1
2
3
# let rec factorial n =
if n <= 1 then 1 else n * factorial (n - 1);;
val factorial : int -> int = <fun>

We can write it using pattern matching instead:

1
2
3
4
5
# let rec factorial n =
match n with
| 0 | 1 -> 1
| x -> x * factorial (x - 1);;
val factorial : int -> int = <fun>

Equally, we could use the pattern _ which matches anything, and write:

1
2
3
4
5
# let rec factorial n =
match n with
| 0 | 1 -> 1
| _ -> n * factorial (n - 1);;
val factorial : int -> int = <fun>

In fact, we may simplify further with the function keyword, which introduces pattern-matching directly:

1
2
3
4
# let rec factorial = function
| 0 | 1 -> 1
| n -> n * factorial (n - 1);;
val factorial : int -> int = <fun>

We will use pattern matching more extensively as we introduce more complicated data structures.

Lists

1
2
3
4
5
6
7
8
9
10
11
12
(* empty list *)
# [];;
- : 'a list = []
(* int list *)
# [1; 2; 3];;
- : int list = [1; 2; 3]
(* bool list *)
# [false; false; true];;
- : bool list = [false; false; true]
(* 2d int list*)
# [[1; 2]; [3; 4]; [5; 6]];;
- : int list list = [[1; 2]; [3; 4]; [5; 6]]

Add elements to list

The ::, or cons operator, adds one element to the front of a list.

1
2
# 1 :: [2; 3];;
- : int list = [1; 2; 3]

Combine two lists

The @, or append operator, combines two lists.

1
2
# [1] @ [2; 3];;
- : int list = [1; 2; 3]

Reference

A First Hour with OCaml · OCaml Tutorials