Note
the separation is with “ ; ” and not “, ” comma
type person = {given:string; sur:string ; age:int}
let paul = {given="Paul",sur="Meier",age=24}
let {given=x;sur = y; age = z} = paul
Output will be val x : string = “Paul” val y : string = “Meier” val z : int = 24
let {given = x ; _ } = paul
match l
with [] -> -1
| x::xs -> x;;
(* -: int = 1 *)```
#### **–Mutually Recursive–** Functions
When functions call each other, they are known as mutually recursive. OCaml supports this through the use of the `and` keyword to tie the recursive functions together:
```ocaml
let rec even n = if n=0 then "even"else odd (n-1)
and odd n = if n=0 then "odd" else even (n-1);;
In this example:
even
checks if a number is even by recursively callingodd
withn-1
.odd
checks if a number is odd by recursively callingeven
withn-1
.
Both functions alternate calls to each other until they reach the base case of checking 0
, returning "even" or "odd" accordingly.
Note
The function
keyword in OCaml is a syntactic sugar that makes defining functions with a single parameter, typically used in pattern matching, more concise. Let’s expand on your examples to illustrate how this simplifies code.
let rec length = fun l -> match l with
| [] -> 0
| x::xs -> 1 + length xs;;
this can be simplified with function as it takes the argument as one keyword already:
let rec length = function
| [] -> 0
| x::xs -> 1 + length xs;;
Understanding when to use fun
and when to use match
with let
in OCaml can initially be confusing, but each serves specific purposes and contexts, enhancing the flexibility and clarity of your code. Here's a breakdown of each and scenarios where you might choose one over the other:
The fun
keyword is used to define anonymous functions in OCaml. It's particularly useful in situations where:
-
You need a quick, inline function that doesn't require naming. For example, passing a small function as an argument to higher-order functions like
List.map
orList.fold
. -
You want to create partial functions or curried functions without explicitly dealing with multiple arguments upfront. For instance, defining functions that return other functions based on some conditions.
Example of using fun
:
List.map (fun x -> x + 1) [1; 2; 3];;
This creates an anonymous function that adds 1 to each element of the list.
match
is used for pattern matching, which is a way to deconstruct and analyse data structures based on their shape. When combined with let
(especially in a function definition), it's powerful for:
-
Handling multiple cases or patterns in data, especially when working with variants or lists. It's useful for writing functions that need to distinguish between different types of input.
-
Recursive function definitions where each recursive call depends on the structure of the input, such as parsing trees, traversing linked lists, etc.
Example of using match
with let
:
let rec find_max = function
| [] -> raise (Failure "empty list")
| [x] -> x
| x :: xs -> max x (find_max xs);;
This function uses match
within a let
to recursively find the maximum in a list.
The function
keyword is a shorthand for defining a function with a single argument that immediately uses match
. It's a concise way to handle functions that:
-
Directly deconstruct their input without needing to refer to the input explicitly by name.
-
Are defined primarily or solely for pattern matching on a single argument.
Example of using function
:
let rec length = function
| [] -> 0
| _ :: xs -> 1 + length xs;;
This function automatically matches on its argument to calculate the length of a list.
-
Use
fun
when you need anonymous functions, especially for small local uses or when you want to return a function from another function. -
Use
match
withlet
when your function needs to handle complex patterns or multiple cases, especially when dealing with data types that require explicit case handling, like lists or custom types. -
Use
function
for a cleaner, more direct syntax when your entire function revolves around pattern matching on a single argument.
By choosing the appropriate construct based on these guidelines, you can make your OCaml code more readable and maintainable.
let calculate_series n =
let rec factorial x =
if x <= 1 then 1 else x * factorial (x - 1)
in
let rec sum_series i acc =
if i > n then acc
else
let term = float_of_int (factorial i) /. float_of_int (i + 1)
in
sum_series (i + 1) (acc +. term)
in
sum_series 1 0.0;;
Note
The use of sum_series (i+1) (acc +. term)
is used for the recursive call of the function
Where as the use of sum_series 1 0.0
is to initiate the recursion
Note
What is a Tail Call? A tail call occurs when a function call is the last operation a function performs before returning its result, and no additional work is needed after the function call returns. In such cases, there's no need to keep the current function's stack frame around, as there are no further computations that depend on it. This allows for an optimisation called tail call elimination or tail call optimisation (TCO), where the program can reuse the stack frame for the called function rather than creating a new one, essentially converting what would be recursive calls into a simple loop under the hood.
![[CleanShot 2024-06-25 at 21.44.33.png]]
- Tail-recursive functions can be executed as efficiently as loops in imperative languages.
- The intermediate results are handed from one recursive call to the next in accumulating parameters.
- From that, a stopping rule computes the result.
- Tail-recursive functions are particularly popular for list processing ...