Introduction to Objective OCaml
Variables and Functions
In ML, variables are names for values. Variable bindings are bind with the keyword \(\text{let}\).
\[ \text{let identifier = expression}\]
Definitions using \(\text{let}\) can also be nested using the \(\text{in}\) form.
\[ \text{let identifier = } \text{expression}_1 \text{ in } \text{expression}_2 \]
The expression \(\text{expression}_2\) is called the body of the \(\text{let}\) The variable named \(\text{identifier}\) is defined as the value of \(\text{expression}_1\) within the body. The \(\text{identifier}\) is defined only in the body \(\text{expression}_2\) and not \(\text{expression}_1\). A \(\text{let}\) with a body is an expression; the value of a \(\text{let}\) expression is the value of the body.
Functions are defined with the keyword \(\text{Fun}\)
\[ \text{fun } v_1 \quad v_2 \quad \cdots\quad v_n \rightarrow expression\]
Higher order functions
Fold right
The map function allows us to modify each item in a list individually, while the filter function enables us to selectively retain or discard each item based on a criterion. However, both functions operate on elements independently. What if our goal it to aggregate all the elements if a list together?
let rec sum = function
0
| [] ->
| (h::t) -> h + sum t
let concat = function
""
| [] -> | (h::t) -> h ^ concat t
For an empty list, a distinct base value is returned: 0 for summing and an empty string (““) for concatenation.
In the case of a non-empty list, a different operation is employed to merge the head of the list with the outcome of the recursive call: addition (+) for summing and concatenation (^) for joining strings.
let rec sum' init = function
| [] -> init
| (h::t) -> h + sum' init t
let sum = sum' 0
let rec concat' init = function
| [] -> init
| (h::t) -> h ^ concat' init t
let concat = concat' ""
Exercise 3.3:
We write a function \(\text{sum}\) that, given two integers bounds n and m and a function f, computes a summation. \[ \text{sum n m f } = \sum_{i=1}^{m} f(i) \]
let sum n m f =
if n > m then
0
else
1) m f f n + sum (n +
Exercise 3.4:
Euclid’s algorithm computes the greatest common divisor (GCD) of two integers.
\[\begin{align*} &\text{gcd}(n, k) = \\ &\quad \text{while } m \neq 0 \\ & \qquad \text{if } n > m \\ & \qquad \quad n \gets n - m \\ & \qquad \text{else} \\ & \qquad \quad m \gets m - n \\ &\quad \text{return } n \end{align*}\]
(* Method 1 *)
let rec gcd n m =
if m = 0 then n
else if n > m then gcd (n - m) m
else gcd n (m - n);;
(* Method 2 *)
let rec gcd n m =
if m = 0 then n
else gcd m (n mod m)
Exercise 3.6:
(* A dictionary is represented as a function from a string to an int *)
type dictionary = string -> int
(* The empty dictionary function that returns 0 for any key *)
let empty : dictonary = fun _ -> 0
(* Function to add key-value pair to a dictiioanry *)
let add d k v = fun k' -> if k' = k then v else d k'
(* Function to find the value associated with a key in a dictionary *)
let find d k : int = d k
Basic pattern matches
One of the most potent capabilities of ML is its utilization of pattern matching for defining computations via case analysis. The syntax for pattern matching is expressed through a match expression, which is outlined as follows.
with
natch expression
| pattern1 -> expression1
| pattern2 -> expression2
.
.
. | pattern_n -> expression_n