OCaml Interview Questions with Answers
Most Asked OCaml Interview Questions for Software Engineer Roles
Introduction
This page provides a complete collection of OCaml Interview Questions and Answers designed for functional programmers, systems developers, and software engineers preparing for technical interviews. OCaml (Objective Caml) is a powerful statically-typed functional programming language with a strong type system, pattern matching, and a sophisticated module system. It is used in finance, formal verification, compiler development, and many other domains. This interview guide covers beginner, intermediate, and advanced OCaml concepts including syntax, recursion, data structures, modules, functors, monads, concurrency, FFI, and real-world functional programming patterns.
Why OCaml?
- Strong static type inference – catches errors at compile time, reduces runtime bugs
- Functional programming paradigm – immutable data, higher-order functions, and pattern matching
- Powerful module system – functors, signatures, and structures for large-scale code organization
- Exceptional performance – compiles to efficient native code or JavaScript (via js_of_ocaml)
- Used in industry – Jane Street, Bloomberg, and many financial firms rely on OCaml
- Growing ecosystem with OPAM and Dune – modern package management and build tools
Most Asked OCaml Interview Questions
OCaml is a statically-typed functional programming language with object-oriented features. It is known for its strong type system, pattern matching, and performance.
- Functional: Functions are first-class citizens
- Statically typed: Type-safe with type inference
- Pattern matching: Powerful for data manipulation
- Module system: Functors, signatures, and structures
- Performance: Compiled to native code
# Hello World in OCaml
print_endline "Hello, World!"Variables in OCaml are immutable by default, declared using the let keyword.
- Declaration:
let x = 10 - Immutable: Variables cannot be reassigned
- Type inference: Types are inferred automatically
- Pattern matching: Can destructure data
- Scope:
let ... infor local scope
# Variables in OCaml
let x = 10 (* Integer *)
let y = 3.14 (* Float *)
let name = "OCaml" (* String *)
let is_active = true (* Boolean *)
let () =
print_int x;
print_char '\n';
print_float y;
print_char '\n';
print_endline name;
print_endline (string_of_bool is_active)OCaml has a rich type system with primitive types, variants, records, and polymorphic types.
- Integers:
int - Floats:
float - Booleans:
bool - Characters:
char - Strings:
string - Tuples:
(int * string) - Lists:
int list - Arrays:
int array - Records:
{ name: string; age: int } - Variants:
type color = Red | Green | Blue
# Data Types in OCaml
# Integer types
let a = 10 (* int *)
let b = 127 (* int *)
# Floating point
let d = 3.14 (* float *)
let e = 2.5 (* float *)
# String
let f = "Hello OCaml"
# Boolean
let g = true
let h = false
# Character
let c = 'A'
# Tuple
let j = (1, "hello", 3.14)
# List
let k = [1; 2; 3; 4; 5]
# Array
let l = [|1; 2; 3; 4; 5|]
# Record type
type person = { name: string; age: int }
let alice = { name = "Alice"; age = 25 }
let () =
print_endline (string_of_int a)Functions in OCaml are defined using the let keyword. Functions can be recursive and can use pattern matching.
- Definition:
let add a b = a + b - Recursive:
let rec factorial n = ... - Pattern matching:
function | ... -> ... - Anonymous functions:
fun x -> x * x - Partial application:
let add5 = add 5
# Functions in OCaml
# Function declaration
let add a b = a + b
# Recursive function
let rec factorial n =
if n <= 1 then 1
else n * factorial (n - 1)
# Function with pattern matching
let rec fibonacci n =
match n with
| 0 -> 0
| 1 -> 1
| n -> fibonacci (n - 1) + fibonacci (n - 2)
# Anonymous function
let square = fun x -> x * x
let double x = x * 2
# Higher-order functions
let apply_twice f x = f (f x)
# Partial application
let add5 = add 5
let () =
print_int (add 5 3);
print_endline "";
print_int (factorial 5);
print_endline "";
print_int (square 4);
print_endline ""Lists in OCaml are immutable, homogeneous, singly-linked lists. They are built using the [] and :: (cons) operators.
- Creation:
[1; 2; 3; 4; 5] - Cons:
0 :: [1; 2; 3] - Concatenation:
[1; 2] @ [3; 4] - Common functions:
List.map,List.filter,List.fold_left - Pattern matching:
match lst with [] -> ... | h :: t -> ...
# Lists in OCaml
let arr = [1; 2; 3; 4; 5]
# Map - transform each element
let doubled = List.map (fun x -> x * 2) arr
let () = List.iter (fun x -> print_int x; print_string " ") doubled
# Filter - select elements
let evens = List.filter (fun x -> x mod 2 = 0) arr
# Reduce - aggregate
let sum = List.fold_left (+) 0 arr
# List comprehension (using List.init)
let squares = List.init 10 (fun i -> (i+1) * (i+1))
# Cons and append
let new_list = 6 :: arr
let combined = arr @ [6; 7; 8]
# List operations
let head = List.hd arr
let tail = List.tl arr
let length = List.length arr
let () =
print_int sum;
print_endline ""Association lists are lists of pairs used as key-value stores. They provide simple dictionary-like functionality.
- Creation:
[("name", "Alice"); ("age", "25")] - Access:
List.assoc "name" dict - Add/Update:
("country", "USA") :: dict - Check:
List.mem_assoc "name" dict - Alternative: Use
Mapmodule for efficiency
# Association Lists (Maps) in OCaml
# Create association list
let person = [("name", "Alice"); ("age", "25"); ("city", "NYC")]
# Access values using List.assoc
let name = List.assoc "name" person
let age = List.assoc "age" person
# Add/update values (creating new list)
let person2 = ("country", "USA") :: person
# Check if key exists
let has_name = List.mem_assoc "name" person
# Iterate over association list
let () =
List.iter (fun (key, value) ->
Printf.printf "%s: %s\n" key value
) person
# Using Map module for efficient key-value pairs
module StringMap = Map.Make(String)
let map = StringMap.empty
let map2 = StringMap.add "name" "Alice" map
let name2 = StringMap.find "name" map2Tuples are immutable ordered collections of values that can have different types.
- Creation:
(1, "hello", 3.14, true) - Access: Use pattern matching
- Unpacking:
let (a, b, c) = tuple - Named tuples: Not supported directly, use records
- Pattern matching:
match (x, y) with (0, 0) -> ...
# Tuples in OCaml
# Create tuple
let t = (1, "hello", 3.14, true)
# Access elements using pattern matching
let (a, b, c, d) = t
let first = let (x, _, _, _) = t in x
# Function returning multiple values
let divide a b = (a / b, a mod b)
let quotient, remainder = divide 10 3
# Tuple of tuples
let nested = ((1, 2), (3, 4))
# Pattern matching on tuples
let describe_tuple (x, y) =
match (x, y) with
| (0, 0) -> "Origin"
| (_, 0) -> "On x-axis"
| (0, _) -> "On y-axis"
| _ -> "Somewhere else"
let () =
Printf.printf "Quotient: %d, Remainder: %d\n" quotient remainderOCaml uses if and match for control flow. Loops are implemented using recursion.
- If-else:
if condition then ... else ... - Pattern matching:
match expression with | ... -> ... - For loops:
for i = 1 to 10 do ... done - While loops:
while condition do ... done - Recursion: Preferred for iteration
# Control Flow in OCaml
# If-else statement
let age = 25 in
let status =
if age < 18 then "Minor"
else if age < 65 then "Adult"
else "Senior"
in
print_endline status
# Pattern matching
let describe_number n =
match n with
| 0 -> "Zero"
| 1 -> "One"
| 2 -> "Two"
| _ -> "Other"
# For loop
let () =
for i = 1 to 5 do
print_int i;
print_char '\n'
done
# While loop
let () =
let i = ref 1 in
while !i <= 5 do
print_int !i;
print_char '\n';
i := !i + 1
done
# Recursion (preferred in OCaml)
let rec print_numbers n =
if n <= 0 then ()
else (
print_int n;
print_char '\n';
print_numbers (n - 1)
)
# Using List.iter for iteration
let fruits = ["apple"; "banana"; "orange"]
let () = List.iter print_endline fruitsOCaml doesn't have built-in list comprehensions but provides similar functionality through higher-order functions.
- Map:
List.map (fun x -> x * x) [1;2;3;4;5] - Filter:
List.filter (fun x -> x mod 2 = 0) [1;2;3;4;5] - Map+Filter: Combine with
|>orList.concat_map - Init:
List.init 10 (fun i -> i+1) - Fold:
List.fold_left (+) 0 [1;2;3;4;5]
# Comprehensions (via List functions) in OCaml
# Using List.init for array comprehension
let squares = List.init 10 (fun i -> (i+1) * (i+1))
# Filter with List.filter
let evens = List.filter (fun x -> x mod 2 = 0) (List.init 20 (fun i -> i+1))
# Map with filter (filtering and transforming)
let even_squares =
List.init 20 (fun i -> i+1)
|> List.filter (fun x -> x mod 2 = 0)
|> List.map (fun x -> x * x)
# Nested loops using List.concat_map
let pairs =
List.concat_map (fun i ->
List.map (fun j -> (i, j)) (List.init 3 (fun k -> k+1))
) (List.init 3 (fun k -> k+1))
# Using List.fold_left for reduction
let sum_squares =
List.init 100 (fun i -> i+1)
|> List.fold_left (+) 0
# Conditional mapping
let results =
List.init 10 (fun i -> i+1)
|> List.map (fun x ->
if x mod 2 = 0 then "even" else "odd"
)
let () =
List.iter (fun x -> print_int x; print_string " ") even_squares;
print_endline ""OCaml provides string manipulation functions including concatenation, substring extraction, and case conversion.
- Concatenation:
^operator - Interpolation: Use
Printf.sprintf - Functions:
String.length,String.uppercase_ascii,String.lowercase_ascii - Substring:
String.sub - Split/Join:
String.split_on_char,String.concat
# Strings in OCaml
# String creation
let str1 = "Hello"
let str2 = "World"
let str3 = "Multi-line\nstring"
# String concatenation
let greeting = str1 ^ " " ^ str2
# String interpolation using Printf
let name = "OCaml"
let version = 4.14
let message = Printf.sprintf "Welcome to %s version %.2f" name version
# String functions
let text = "Hello, World!"
let length = String.length text
let uppercase = String.uppercase_ascii text
let lowercase = String.lowercase_ascii text
# Substring
let sub = String.sub text 0 5
# Split and join
let words = String.split_on_char ' ' "Hello World OCaml"
let joined = String.concat "-" words
# String comparison
let cmp1 = "hello" = "hello"
let cmp2 = "hello" < "world"
# Character operations
let first_char = text.[0]
let char_code = int_of_char 'A'
# String formatting
let formatted = Printf.sprintf "Value: %.2f" 3.14159
let () =
print_endline greeting;
print_endline message;
print_endline uppercaseModules in OCaml provide a way to organize code, encapsulate implementation details, and define interfaces.
- Definition:
module MyModule = struct ... end - Signature:
module type NAME = sig ... end - Functors: Modules parameterized by modules
- Include:
include Module - Open:
open Module
# Modules in OCaml
# Defining a module using struct
module MyMath = struct
let pi = 3.14159
let add a b = a + b
let subtract a b = a - b
let multiply a b = a * b
let divide a b = a / b
end
# Using a module
let result = MyMath.add 5 3
let pi_value = MyMath.pi
# Module signature (interface)
module type MATH = sig
val pi : float
val add : int -> int -> int
val subtract : int -> int -> int
end
# Implementing a module with signature
module MyMath2 : MATH = struct
let pi = 3.14159
let add a b = a + b
let subtract a b = a - b
let multiply a b = a * b (* Hidden from signature *)
end
# Functors (modules parameterized by modules)
module type ORDERED = sig
type t
val compare : t -> t -> int
end
module Set = functor (M: ORDERED) -> struct
type t = M.t list
let empty = []
let add x set = x :: set
end
# Opening modules
open MyMath
let sum = add 10 5
let () =
print_int result;
print_endline ""OCaml has a powerful type system with built-in and user-defined types. Types are inferred but can also be explicitly specified.
- Built-in types:
int,float,string,bool - Variant types:
type color = Red | Green | Blue - Record types:
type person = { name: string; age: int } - Polymorphic types:
'a option - Abstract types: Defined in signatures
# Types in OCaml
# Basic type definitions
type int_list = int list
type string_pair = string * string
# Variant types (sum types)
type color =
| Red
| Green
| Blue
| RGB of int * int * int
# Record types (product types)
type person = {
name: string;
age: int;
city: string;
}
# Type with parameters (polymorphic)
type 'a option =
| None
| Some of 'a
# Recursive types
type tree =
| Empty
| Node of int * tree * tree
# Type abbreviations
type point = float * float
# Abstract types
module type ABSTRACT = sig
type t
val create : int -> t
val value : t -> int
end
# Usage
let alice = { name = "Alice"; age = 25; city = "NYC" }
let color = RGB(255, 0, 0)
# Pattern matching on types
let get_name person = person.name
let get_age { age; _ } = age
let describe_color = function
| Red -> "Red"
| Green -> "Green"
| Blue -> "Blue"
| RGB(r, g, b) -> Printf.sprintf "RGB(%d, %d, %d)" r g b
let () =
print_endline (describe_color color);
print_int (get_age alice);
print_endline ""Pattern matching is a core feature of OCaml that allows deconstruction of data structures and control flow based on structure.
- Basic:
match x with | 0 -> "zero" | _ -> "non-zero" - Lists:
match lst with [] -> ... | h :: t -> ... - Records:
match person with { name; age } -> ... - Guards:
match n with x when x < 0 -> "negative" - Nested: Pattern matching can be nested
# Pattern Matching in OCaml
# Basic pattern matching
let is_zero n =
match n with
| 0 -> true
| _ -> false
# Pattern matching on lists
let rec sum_list lst =
match lst with
| [] -> 0
| head :: tail -> head + sum_list tail
# Pattern matching with guards
let classify_number n =
match n with
| 0 -> "Zero"
| x when x < 0 -> "Negative"
| x when x > 0 -> "Positive"
| _ -> "Unknown"
# Pattern matching on tuples
let add_tuple (x, y) = x + y
# Pattern matching on records
let describe_person { name; age; city } =
Printf.sprintf "%s is %d years old from %s" name age city
# Nested pattern matching
let rec sum_tree = function
| Empty -> 0
| Node(value, left, right) ->
value + sum_tree left + sum_tree right
# Pattern matching with OR patterns
let is_zero_or_one n =
match n with
| 0 | 1 -> true
| _ -> false
# Function with multiple patterns
let rec fibonacci n =
match n with
| 0 -> 0
| 1 -> 1
| n -> fibonacci (n - 1) + fibonacci (n - 2)
let () =
print_int (sum_list [1; 2; 3; 4; 5]);
print_endline ""OCaml provides exception handling using try and with blocks, with custom exceptions and pattern matching.
- Definition:
exception MyException of string - Raise:
raise (MyException "error") - Catch:
try ... with | MyException msg -> ... - Finally: Use nested try blocks
- Print:
Printexc.to_string
# Exception Handling in OCaml
# Exception definition
exception Division_by_zero
exception Invalid_input of string
# Raising exceptions
let divide a b =
if b = 0 then raise Division_by_zero
else a / b
# Try-catch block
let safe_divide a b =
try
Some (divide a b)
with
| Division_by_zero -> None
| Invalid_input msg ->
Printf.printf "Invalid input: %s\n" msg;
None
# Multiple exception handlers
let process_file filename =
try
let ic = open_in filename in
let content = really_input_string ic (in_channel_length ic) in
close_in ic;
content
with
| Sys_error msg ->
Printf.printf "System error: %s\n" msg;
""
| End_of_file ->
Printf.printf "Empty file\n";
""
| e ->
Printf.printf "Unexpected error: %s\n" (Printexc.to_string e);
""
# Using finally pattern
let with_file filename f =
let ic = open_in filename in
try
let result = f ic in
close_in ic;
result
with e ->
close_in ic;
raise e
# Custom exception with data
exception Error of { code: int; message: string }
let handle_error e =
match e with
| Error { code; message } ->
Printf.printf "Error %d: %s\n" code message
| _ -> print_endline "Unknown error"
let () =
match safe_divide 10 0 with
| Some result -> print_int result
| None -> print_endline "Cannot divide by zero"OCaml provides standard library functions for file I/O using channels. Common operations include reading, writing, and appending.
- Read:
open_in,input_line,close_in - Write:
open_out,output_string,close_out - Append:
open_out_gen [Open_append] - CSV: Manual parsing or using libraries
- Try-catch: Handle file errors
# File I/O in OCaml
# Reading files
let read_file filename =
let ic = open_in filename in
let content = really_input_string ic (in_channel_length ic) in
close_in ic;
content
# Reading line by line
let read_lines filename =
let ic = open_in filename in
let lines = ref [] in
try
while true do
lines := input_line ic :: !lines
done;
[]
with End_of_file ->
close_in ic;
List.rev !lines
# Writing files
let write_file filename content =
let oc = open_out filename in
output_string oc content;
close_out oc
# Appending to files
let append_file filename content =
let oc = open_out_gen [Open_append; Open_creat; Open_text] 0o644 filename in
output_string oc content;
close_out oc
# Using with_file pattern
let with_output_file filename f =
let oc = open_out filename in
try
let result = f oc in
close_out oc;
result
with e ->
close_out oc;
raise e
# Reading CSV (simple implementation)
let read_csv_line line =
String.split_on_char ',' line
# Writing CSV
let write_csv_row oc row =
output_string oc (String.concat "," row);
output_char oc '\n'
let () =
write_file "example.txt" "Hello, World!\nThis is line 2\n";
let content = read_file "example.txt" in
print_endline contentOCaml uses the OPAM package manager and Dune build system for managing and building projects.
- OPAM: Package manager
- Install:
opam install package - Dune: Build system
- Libraries:
open Base,open Core - Dune file:
(libraries base core)
# Packages in OCaml
# Using Dune (build system)
# To add a package, modify the dune file:
# (executable
# (name main)
# (libraries base core))
# Using OPAM (package manager)
# Install packages:
# opam install core base ppx_jane
# Using packages in code
open Core
open Base
# Using Jane Street's Base library
let () =
let numbers = [1; 2; 3; 4; 5] in
let doubled = List.map ~f:(fun x -> x * 2) numbers in
List.iter ~f:(fun x -> printf "%d " x) doubled
# Using Core library
let () =
let numbers = [1; 2; 3; 4; 5] in
let sum = List.fold numbers ~init:0 ~f:(+) in
printf "Sum: %d\n" sum
# Using Lwt for asynchronous programming
open Lwt.Infix
let async_example () =
Lwt_io.print "Hello " >>=
fun () -> Lwt_io.print "World!\n"
# Using Yojson for JSON
open Yojson
let json_data = `Assoc [
("name", `String "Alice");
("age", `Int 25);
("city", `String "NYC")
]
let () =
let json_string = Yojson.Safe.to_string json_data in
print_endline json_stringOCaml can create plots using libraries like Plotly, GNUplot, or the built-in Graphics module.
- Plotly: Interactive plots
- GNUplot: System command integration
- Graphics: Built-in 2D graphics
- Asmlib-plot: Scientific plotting
- Labels: Axis labels and titles
# Plotting in OCaml
# Using Plotly package
open Plotly
let plot_example () =
let x = [1.; 2.; 3.; 4.; 5.] in
let y = List.map (fun x -> x *. x) x in
let trace = Trace.scatter ~x ~y ~mode:`Markers () in
let layout = Layout.create ~title:"Square Function" () in
Plotly.show [trace] layout
# Using GNU Plot
let plot_with_gnuplot () =
let data = [(1., 1.); (2., 4.); (3., 9.); (4., 16.)] in
let () =
let oc = open_out "plot.dat" in
List.iter (fun (x, y) ->
Printf.fprintf oc "%f %f\n" x y
) data;
close_out oc
in
Sys.command "gnuplot -e 'plot "plot.dat" with lines'"
# Using Graphics library (built-in)
open Graphics
let draw_plot () =
open_graph " 640x480";
let width = 640 in
let height = 480 in
let scale_x = float width /. 10. in
let scale_y = float height /. 100. in
for i = 0 to 100 do
let x = float i /. 10. in
let y = x *. x in
let screen_x = int_of_float (x *. scale_x) in
let screen_y = int_of_float (y *. scale_y) in
plot screen_x screen_y
done;
ignore (read_key ())
# Using Asmlib for more advanced plotting
# Requires: opam install asmlib-plot
let () =
Printf.printf "Plotting examples available with installed packages\n"OCaml provides various data structures through its standard library and Base/Core libraries including maps, sets, hashtables, queues, and stacks.
- Map:
Map.Make(String) - Set:
Set.Make(String) - Hashtbl:
Hashtbl.create - Queue:
Queue.create - Stack:
Stack.create
# Data Structures in OCaml
# Using Base library's Data Structures
open Core
# Map (from Base)
module StringMap = Map.Make(String)
let map_example () =
let map = StringMap.empty in
let map = StringMap.set map ~key:"Alice" ~data:25 in
let map = StringMap.set map ~key:"Bob" ~data:30 in
let alice_age = StringMap.find_exn map "Alice" in
printf "Alice's age: %d\n" alice_age
# Set (from Base)
module StringSet = Set.Make(String)
let set_example () =
let set = StringSet.empty in
let set = StringSet.add set "Alice" in
let set = StringSet.add set "Bob" in
let has_alice = StringSet.mem set "Alice" in
printf "Has Alice: %b\n" has_alice
# Hashtbl
let hash_example () =
let table = Hashtbl.create (module String) in
Hashtbl.set table ~key:"Alice" ~data:25;
Hashtbl.set table ~key:"Bob" ~data:30;
let alice_age = Hashtbl.find_exn table "Alice" in
printf "Alice's age: %d\n" alice_age
# Queue
let queue_example () =
let q = Queue.create () in
Queue.enqueue q 1;
Queue.enqueue q 2;
Queue.enqueue q 3;
while not (Queue.is_empty q) do
let x = Queue.dequeue_exn q in
printf "%d " x
done;
printf "\n"
# Stack
let stack_example () =
let s = Stack.create () in
Stack.push s 1;
Stack.push s 2;
Stack.push s 3;
while not (Stack.is_empty s) do
let x = Stack.pop_exn s in
printf "%d " x
done;
printf "\n"
let () =
map_example ();
set_example ();
hash_example ();
queue_example ();
stack_example ()OCaml provides statistical functions through its standard library and additional packages like Owl.
- Mean:
List.fold_left - Median:
List.nthafter sorting - Standard deviation: Calculate from mean
- Correlation: Using
List.fold - Owl: Advanced statistical functions
# Statistics in OCaml
# Using Base library for statistics
open Base
let basic_stats data =
let n = List.length data in
let sum = List.fold data ~init:0 ~f:(+) in
let mean = float sum /. float n in
let variance =
List.fold data ~init:0.0 ~f:(fun acc x ->
let diff = float x -. mean in
acc +. (diff *. diff)
) /. float n
in
let std_dev = sqrt variance in
(mean, variance, std_dev)
# Median calculation
let median data =
let sorted = List.sort ~compare:Int.compare data in
let n = List.length sorted in
if n mod 2 = 1 then
let index = n / 2 in
float (List.nth_exn sorted index)
else
let index1 = n / 2 - 1 in
let index2 = n / 2 in
let val1 = List.nth_exn sorted index1 in
let val2 = List.nth_exn sorted index2 in
float (val1 + val2) /. 2.0
# Correlation
let correlation xs ys =
let n = List.length xs in
let sum_x = List.fold xs ~init:0 ~f:(+) in
let sum_y = List.fold ys ~init:0 ~f:(+) in
let mean_x = float sum_x /. float n in
let mean_y = float sum_y /. float n in
let sum_xy = List.fold2_exn xs ys ~init:0.0 ~f:(fun acc x y ->
acc +. (float x -. mean_x) *. (float y -. mean_y)
) in
let sum_x2 = List.fold xs ~init:0.0 ~f:(fun acc x ->
acc +. (float x -. mean_x) *. (float x -. mean_x)
) in
let sum_y2 = List.fold ys ~init:0.0 ~f:(fun acc y ->
acc +. (float y -. mean_y) *. (float y -. mean_y)
) in
sum_xy /. sqrt (sum_x2 *. sum_y2)
# Random data
let () =
let data = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10] in
let mean, variance, std_dev = basic_stats data in
Printf.printf "Mean: %f\n" mean;
Printf.printf "Variance: %f\n" variance;
Printf.printf "Std Dev: %f\n" std_dev;
Printf.printf "Median: %f\n" (median data)OCaml provides linear algebra operations through the Owl library or by implementing matrix operations manually.
- Owl:
Mat.of_arrays,Mat.dot - Matrix multiplication: Implement manually
- Eigenvalues:
Mat.eigvals - Determinant:
Mat.det - Inverse:
Mat.inv
# Linear Algebra in OCaml
# Using Owl library for linear algebra
# (Requires: opam install owl)
open Owl
let linear_algebra_example () =
let a = Mat.of_arrays [|
[|1.; 2.; 3.|];
[|4.; 5.; 6.|];
[|7.; 8.; 10.|]
|] in
let b = Mat.of_arrays [| [|1.|]; [|2.|]; [|3.|] |] in
# Matrix operations
let product = Mat.dot a b in
let transpose = Mat.transpose a in
# Solve linear systems
let x = Mat.linsolve a b in
# Eigenvalues
let eigenvals = Mat.eigvals a in
# Determinant
let det = Mat.det a in
# Inverse
let inv = Mat.inv a in
# Matrix functions
let eye = Mat.eye 3 in
let zeros = Mat.zeros 3 3 in
let ones = Mat.ones 3 3 in
Printf.printf "Determinant: %f\n" det
# Using Linalg module from Base
open Base
let simple_linear_algebra () =
let a = [| [|1.; 2.|]; [|3.; 4.|] |] in
let b = [| [|5.|]; [|6.|] |] in
# Matrix multiplication
let multiply a b =
let n = Array.length a in
let m = Array.length b.(0) in
let result = Array.make_matrix n m 0.0 in
for i = 0 to n-1 do
for j = 0 to m-1 do
let sum = ref 0.0 in
for k = 0 to Array.length a.(0)-1 do
sum := !sum +. a.(i).(k) *. b.(k).(j)
done;
result.(i).(j) <- !sum
done
done;
result
in
let result = multiply a b in
Printf.printf "Matrix multiplication done\n"
let () =
linear_algebra_example ();
simple_linear_algebra ()OCaml provides date handling through the Unix module and the Calendar library for more advanced operations.
- Current time:
Unix.time - Date components:
Unix.localtime - Date arithmetic: Add seconds directly
- Calendar:
Date.add,Date.diff - Formatting:
string_of_time
# Dates and Time in OCaml
# Using Unix module (built-in)
open Unix
let date_example () =
# Current time
let now = time () in
let local_time = localtime now in
# Getting components
let year = local_time.tm_year + 1900 in
let month = local_time.tm_mon + 1 in
let day = local_time.tm_mday in
let hour = local_time.tm_hour in
let minute = local_time.tm_min in
Printf.printf "Current date: %d-%02d-%02d %02d:%02d\n"
year month day hour minute
# Date arithmetic
let date_arithmetic () =
let now = time () in
let one_day = 24.0 *. 60.0 *. 60.0 in
let tomorrow = now +. one_day in
let yesterday = now -. one_day in
let fmt = "%Y-%m-%d %H:%M:%S" in
Printf.printf "Now: %s\n" (string_of_time now);
Printf.printf "Tomorrow: %s\n" (string_of_time tomorrow);
Printf.printf "Yesterday: %s\n" (string_of_time yesterday)
# Using Calendar library
# (Requires: opam install calendar)
open CalendarLib
let calendar_example () =
let date = Date.make 2024 1 1 in
let date_plus_10 = Date.add date (Date.Period.days 10) in
let date_plus_2_months = Date.add date (Date.Period.months 2) in
Printf.printf "Date: %s\n" (Date.to_string date);
Printf.printf "Date + 10 days: %s\n" (Date.to_string date_plus_10);
Printf.printf "Date + 2 months: %s\n" (Date.to_string date_plus_2_months)
# Time difference
let time_difference date1 date2 =
let diff = Date.diff date1 date2 in
(diff / 86400, diff mod 86400 / 3600)
let () =
date_example ();
date_arithmetic ();
calendar_example ()OCaml provides regular expression functionality through the Str module and other libraries like Pcre.
- Create:
Str.regexp - Match:
Str.string_match - Capture groups:
Str.matched_group - Replace:
Str.global_replace - Case insensitive:
Str.regexp_case_fold
# Regular Expressions in OCaml
# Using Str module (built-in)
open Str
let regex_example () =
# Create regex
let re = regexp "hello" in
let text = "hello world" in
# Match
let match_result = string_match re text 0 in
Printf.printf "Match found: %b\n" match_result
# Find all matches
let text2 = "hello world hello again" in
let matches = ref [] in
let start = ref 0 in
while string_match (regexp "hello") text2 !start do
matches := (matched_string text2) :: !matches;
start := match_end ()
done;
List.iter print_endline !matches
# Regex with capture groups
let re2 = regexp "\([0-9][0-9][0-9][0-9]\)-\([0-9][0-9]\)-\([0-9][0-9]\)" in
let text3 = "Date: 2024-01-01" in
if string_match re2 text3 0 then
begin
let year = matched_group 1 text3 in
let month = matched_group 2 text3 in
let day = matched_group 3 text3 in
Printf.printf "Year: %s, Month: %s, Day: %s\n" year month day
end
# Replace with regex
let replaced = global_replace (regexp "[0-9]+") "NUM" "Hello 123 World" in
Printf.printf "Replaced: %s\n" replaced
# Case insensitive
let re3 = regexp_case_fold "hello" in
let matched = string_match re3 "HELLO world" 0 in
Printf.printf "Case insensitive match: %b\n" matched
# Split
let parts = split (regexp "[, ]+") "Hello World OCaml" in
List.iter (fun s -> Printf.printf "%s " s) parts;
print_endline ""
let () =
regex_example ()OCaml supports parallel computing through domains (OCaml 5.0+), Lwt for concurrency, and Async for asynchronous programming.
- Domains:
Domain.spawn,Domain.join - Lwt: Lightweight threads
- Async: Jane Street's asynchronous library
- Parallel map: Domain.map
- Channels: Communication between domains
# Parallel Computing in OCaml
# Using Domain module (OCaml 5.0+)
let parallel_example () =
# Create domains for parallel execution
let domain1 = Domain.spawn (fun () ->
let result = ref 0 in
for i = 1 to 1000000 do
result := !result + i
done;
!result
) in
let domain2 = Domain.spawn (fun () ->
let result = ref 0 in
for i = 1000001 to 2000000 do
result := !result + i
done;
!result
) in
let sum1 = Domain.join domain1 in
let sum2 = Domain.join domain2 in
Printf.printf "Sum: %d\n" (sum1 + sum2)
# Using Async for concurrent programming
open Async
let async_example () =
let task1 () =
after (Time.Span.of_sec 2.0) >>= fun () ->
printf "Task 1 completed\n";
return 42
in
let task2 () =
after (Time.Span.of_sec 1.0) >>= fun () ->
printf "Task 2 completed\n";
return 100
in
Deferred.both (task1 ()) (task2 ()) >>= fun (result1, result2) ->
printf "Results: %d, %d\n" result1 result2;
return ()
# Using Lwt for lightweight threads
open Lwt
let lwt_example () =
let task1 () =
Lwt_unix.sleep 2.0 >>= fun () ->
Lwt_io.printf "Task 1 completed\n" >>= fun () ->
Lwt.return 42
in
let task2 () =
Lwt_unix.sleep 1.0 >>= fun () ->
Lwt_io.printf "Task 2 completed\n" >>= fun () ->
Lwt.return 100
in
Lwt.both (task1 ()) (task2 ()) >>= fun (result1, result2) ->
Lwt_io.printf "Results: %d, %d\n" result1 result2
# Parallel map using domains
let parallel_map f lst =
let chunk_size = List.length lst / Domain.recommended_domain_count () in
let chunks = List.fold_left (fun acc x ->
match acc with
| [] -> [[x]]
| h :: t ->
if List.length h < chunk_size then (x :: h) :: t
else [x] :: h :: t
) [] lst
in
let domains = List.map (fun chunk ->
Domain.spawn (fun () -> List.map f (List.rev chunk))
) chunks
in
List.concat (List.map Domain.join domains)
let () =
parallel_example ();
let result = parallel_map (fun x -> x * x) [1;2;3;4;5;6;7;8;9;10] in
List.iter (fun x -> Printf.printf "%d " x) result;
print_endline ""OCaml supports metaprogramming through PPX (preprocessor extensions) and camlp4 for syntax extensions and code generation.
- PPX: Preprocessor extensions
- ppx_deriving: Derive functions
- ppx_jane: Jane Street's PPX
- ppx_sexp: S-expression serialization
- ppx_compare: Derive comparison functions
# Metaprogramming in OCaml
# Using Camlp4 or PPX for metaprogramming
# Example of a simple PPX rewriter
# (Save as ppx_example.ml)
open Ast_helper
open Parsetree
let rewrite_expression expr =
match expr with
| { pexp_desc = Pexp_ident { txt = Longident.Lident "debug" } } ->
pexp_extension (mkloc "debug" Location.none) []
| _ -> expr
# Using ppx_deriving for deriving functions
# (Requires: opam install ppx_deriving)
type person = {
name: string;
age: int;
} [@@deriving show, eq, ord]
let () =
let alice = { name = "Alice"; age = 25 } in
Printf.printf "%s\n" (show_person alice);
let bob = { name = "Bob"; age = 30 } in
Printf.printf "Alice == Bob: %b\n" (person_equal alice bob);
Printf.printf "Alice < Bob: %b\n" (person_compare alice bob < 0)
# Using ppx_jane (Jane Street's PPX)
open Core
let ppx_jane_example () =
# Creating a list
let numbers = [%list: 1; 2; 3; 4; 5] in
# Pattern matching with ppx
let sum = function
| [%list: x; y; z] -> x + y + z
| _ -> 0
in
Printf.printf "Sum: %d\n" (sum numbers)
# Using quotation
let quotation_example () =
let exp = <:expr< 1 + 2 >> in
let result = eval_expr exp in
Printf.printf "Result: %d\n" result
# Custom PPX for logging
# (Example of a PPX rewriter for logging)
let () =
Printf.printf "Metaprogramming examples\n"OCaml provides multiple ways to interface with C including Ctypes for dynamic binding and C stubs for static linking.
- Ctypes:
foreign,Dl.dlopen - C stubs:
externaldeclarations - Compilation:
ocamlc -custom - Caml headers:
caml/mlvalues.h - Memory management:
CAMLparam,CAMLreturn
# Interoperability with C in OCaml
# Using Ctypes library
# (Requires: opam install ctypes)
open Ctypes
open Foreign
let c_example () =
# Load C library
let lib = Dl.dlopen ~filename:"libm.so.6" ~flags:[Dl.RTLD_LAZY] in
# Define C function
let sin = foreign "sin" ~from:lib (double @-> returning double) in
# Call C function
let result = sin 0.5 in
Printf.printf "sin(0.5) = %f\n" result
# Using C stubs
# (Create a C file: mylib.c)
# int add(int a, int b) { return a + b; }
# (Create OCaml file with external declaration)
external add: int -> int -> int = "caml_add"
let stub_example () =
let result = add 5 3 in
Printf.printf "5 + 3 = %d\n" result
# Using OCaml's C interface
# (Compile with: ocamlc -custom mylib.o mylib.ml)
# Example of C stub code
# (Save as mylib.c)
# #include <caml/mlvalues.h>
# #include <caml/alloc.h>
# #include <caml/memory.h>
# #include <caml/callback.h>
#
# CAMLprim value caml_add(value a, value b) {
# CAMLparam2(a, b);
# CAMLreturn(Val_int(Int_val(a) + Int_val(b)));
# }
let () =
c_example ();
stub_example ()OCaml performance can be optimized through tail recursion, avoiding unnecessary allocations, using arrays, and compiler flags.
- Tail recursion:
let rec helper acc ... - Avoid allocations: Use arrays for performance
- Inline:
[@inline] - Compiler flags:
-O3,-flambda - Gc:
Gc.compactfor memory management
# Performance Optimization in OCaml
# Performance tips
# 1. Use tail recursion
let rec sum_list_tail lst acc =
match lst with
| [] -> acc
| h :: t -> sum_list_tail t (acc + h)
# 2. Use immutable data structures carefully
let immutable_example () =
let lst = [1; 2; 3] in
let new_lst = 0 :: lst in (* O(1) cons operation *)
List.iter (fun x -> Printf.printf "%d " x) new_lst;
print_endline ""
# 3. Avoid unnecessary allocations
let sum_array arr =
let total = ref 0 in
for i = 0 to Array.length arr - 1 do
total := !total + arr.(i)
done;
!total
# 4. Use arrays for performance-critical code
let array_example () =
let arr = Array.init 1000000 (fun i -> i) in
let sum = sum_array arr in
Printf.printf "Sum: %d\n" sum
# 5. Use Flambda optimization
# (Compile with: ocamlopt -O3 -flambda)
# 6. Use inline functions
let (@@) f x = f x (* Inline composition *)
# 7. Avoid polymorphic comparisons
let compare_int (x: int) (y: int) = x = y
# 8. Use unboxed types when possible
type 'a unboxed = private int
# 9. Profiling with gprof
# (Compile with: ocamlopt -p -o program program.ml)
# 10. Use Gc module for memory management
open Gc
let gc_example () =
compact ();
Printf.printf "Memory compacted\n"
let () =
let sum = sum_list_tail [1;2;3;4;5] 0 in
Printf.printf "Sum: %d\n" sum;
array_example ();
gc_example ()OCaml supports networking through various libraries including Cohttp for HTTP, Lwt for asynchronous networking, and the Unix module for TCP/UDP.
- HTTP:
Cohttp_lwt_unix.Client.get - WebSockets:
WebSocket.Server.create - TCP:
Unix.socket,Unix.connect - Server:
Unix.listen,Unix.accept - Async:
Lwt.async
# Networking in OCaml
# Using Lwt for HTTP client
open Lwt
open Cohttp_lwt_unix
let http_client_example () =
let uri = Uri.of_string "https://api.github.com" in
Client.get uri >>= fun (response, body) ->
Body.to_string body >>= fun body_text ->
Lwt_io.printf "Response: %s\n" body_text
# HTTP Server
let http_server_example () =
let callback _conn req body =
let uri = req |> Request.uri in
let path = Uri.path uri in
let response_body = match path with
| "/" -> "Hello, World!"
| "/api" -> "API Endpoint"
| _ -> "Not Found"
in
Server.respond_string ~status:`OK ~body:response_body ()
in
let server = Server.create ~mode:(`TCP (`Port 8080)) (Server.make ~callback ()) in
Lwt.return ()
# WebSocket example
open WebSocket
let websocket_example () =
let server = WebSocket.Server.create ~port:8080 in
WebSocket.Server.on_connection server (fun ws ->
WebSocket.on_message ws (fun msg ->
Printf.printf "Received: %s\n" msg;
WebSocket.send ws ("Echo: " ^ msg)
)
)
# TCP client
let tcp_client_example () =
let host = "example.com" in
let port = 80 in
Unix.(gethostbyname host).h_addr_list.(0)
|> fun addr -> ADDR_INET (addr, port)
|> fun sockaddr ->
let sock = Unix.socket PF_INET SOCK_STREAM 0 in
Unix.connect sock sockaddr;
let msg = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n" in
let _ = Unix.send sock msg 0 (String.length msg) [] in
let response = String.make 1024 '\000' in
let len = Unix.recv sock response 0 1024 [] in
Printf.printf "%s\n" (String.sub response 0 len);
Unix.close sock
# TCP server
let tcp_server_example () =
let server = Unix.socket PF_INET SOCK_STREAM 0 in
Unix.setsockopt server SO_REUSEADDR true;
Unix.bind server (ADDR_INET (Unix.inet_addr_any, 8080));
Unix.listen server 5;
while true do
let (client, _) = Unix.accept server in
let msg = "Hello from server!\n" in
ignore (Unix.send client msg 0 (String.length msg) []);
Unix.close client
done
let () =
Printf.printf "Networking examples available\n"OCaml provides JSON support through the Yojson library with functions for encoding and decoding JSON data.
- Parse:
Yojson.Safe.from_string - File:
Yojson.Safe.to_stringwith output - Pattern matching:
match json with `Assoc fields -> ...
# Working with JSON in OCaml
# Using Yojson library
# (Requires: opam install yojson)
open Yojson
let json_example () =
# Encoding to JSON
let data = `Assoc [
("name", `String "Alice");
("age", `Int 25);
("city", `String "NYC");
("hobbies", `List [
`String "reading";
`String "coding"
])
] in
let json_string = Yojson.Safe.to_string data in
Printf.printf "%s\n" json_string;
# Pretty print
let pretty_json = Yojson.Safe.to_string_pretty data in
Printf.printf "%s\n" pretty_json;
# Decoding from JSON
let json_str = "{"name":"Bob","age":30,"city":"LA"}" in
let parsed = Yojson.Safe.from_string json_str in
match parsed with
| `Assoc fields ->
List.iter (fun (key, value) ->
match key, value with
| "name", `String name -> Printf.printf "Name: %s\n" name
| "age", `Int age -> Printf.printf "Age: %d\n" age
| "city", `String city -> Printf.printf "City: %s\n" city
| _ -> ()
) fields
| _ -> ()
# Working with arrays
let json_array = Yojson.Safe.to_string (`List [
`Int 1; `Int 2; `Int 3; `Int 4; `Int 5
]) in
Printf.printf "%s\n" json_array;
let parsed_array = Yojson.Safe.from_string json_array in
match parsed_array with
| `List items ->
List.iter (function
| `Int i -> Printf.printf "%d " i
| _ -> ()
) items;
print_endline ""
| _ -> ()
# Nested structures
let nested = `Assoc [
("user", `Assoc [
("id", `Int 1);
("profile", `Assoc [
("name", `String "Alice");
("email", `String "alice@example.com")
])
])
] in
Printf.printf "%s\n" (Yojson.Safe.to_string_pretty nested)
# Using with files
let json_file_example () =
# Write to file
let data = `Assoc [("name", `String "Alice"); ("age", `Int 25)] in
let oc = open_out "data.json" in
output_string oc (Yojson.Safe.to_string_pretty data);
close_out oc;
# Read from file
let ic = open_in "data.json" in
let content = really_input_string ic (in_channel_length ic) in
close_in ic;
let parsed = Yojson.Safe.from_string content in
Printf.printf "%s\n" (Yojson.Safe.to_string parsed)
let () =
json_example ();
json_file_example ()OCaml testing is done using libraries like Alcotest for unit testing and QCheck for property-based testing.
- Alcotest:
Alcotest.(check int) - Test sets:
test_case,test_raises - QCheck: Property-based testing
- Test suite:
Alcotest.run - Floating point: Use epsilon for comparison
# Testing in OCaml
# Using Alcotest for testing
# (Requires: opam install alcotest)
open Alcotest
# Basic tests
let test_math () =
check int "1 + 1 = 2" (1 + 1) 2;
check int "2 * 3 = 6" (2 * 3) 6
# Floating point tests
let test_float () =
check (float 0.0001) "0.1 + 0.2 ≈ 0.3" (0.1 +. 0.2) 0.3
# Test with exceptions
let test_divide () =
let divide a b =
if b = 0 then raise Division_by_zero
else a / b
in
check int "10 / 2 = 5" (divide 10 2) 5;
check_raises "Division by zero" Division_by_zero (fun () -> divide 10 0)
# Test with collections
let test_list () =
let lst = [1; 2; 3] in
check int "List length" (List.length lst) 3;
check int "List sum" (List.fold_left (+) 0 lst) 6
# Test with records
type person = { name: string; age: int }
let test_record () =
let alice = { name = "Alice"; age = 25 } in
check string "Person name" alice.name "Alice";
check int "Person age" alice.age 25
# Property-based testing with QCheck
# (Requires: opam install qcheck)
open QCheck
let prop_associative () =
Test.make ~count:1000
(Gen.int_range 1 100)
(fun x -> x + 0 = x)
# Test suite
let suite = [
"Math tests", [
test_case "Basic math" `Quick test_math;
test_case "Floating point" `Quick test_float;
test_case "Division" `Quick test_divide;
];
"Data structure tests", [
test_case "List operations" `Quick test_list;
test_case "Record operations" `Quick test_record;
]
]
let () =
run "OCaml Test Suite" suiteOCaml provides debugging through Printf, assertions, the built-in debugger (ocamldebug), and logging.
- Printf:
printf "debug: %d\n" x - Assertions:
assert (x > 0) - Debugger:
ocamldebug - Logging: Custom logging functions
- Exceptions:
Printexc.to_string
# Debugging in OCaml
# Using Printf for debugging
open Printf
let debug_example () =
let x = 10 in
let y = 20 in
printf "x + y = %d\n" (x + y)
# Using Format for structured output
open Format
let format_example () =
let x = 10 in
let y = 20 in
printf "@[<h>Value: %d, %d@]" x y
# Using OCaml's built-in debugger
# (Compile with: ocamlc -g program.ml)
# (Run: ocamldebug program)
# Using Printf with locations
let debug_with_location msg value =
printf "[DEBUG] %s: %d\n" msg value
# Using assertion
let assert_example () =
let x = 10 in
assert (x > 0);
printf "x is positive: %d\n" x
# Using Logging
module Log = struct
let level = ref 2 (* 0=error, 1=warning, 2=info, 3=debug *)
let log level msg =
if level <= !level then
Printf.printf "[%s] %s\n"
(match level with
| 0 -> "ERROR"
| 1 -> "WARN"
| 2 -> "INFO"
| 3 -> "DEBUG"
| _ -> "UNKNOWN")
msg
end
let logging_example () =
Log.log 2 "Processing data";
Log.log 3 "Detailed information"
# Using Printexc for exception tracing
let exception_example () =
try
let _ = 10 / 0 in
()
with e ->
Printf.printf "Exception: %s\n" (Printexc.to_string e);
Printf.printf "Backtrace: %s\n" (Printexc.get_backtrace ())
# Using ocaml-profiler for performance
# (Compile with: ocamlopt -p program.ml)
let () =
debug_example ();
assert_example ();
logging_example ();
exception_example ()Abstract types and interfaces in OCaml are defined using module signatures to hide implementation details while providing a contract.
- Signature:
module type NAME = sig ... end - Abstract type:
type twithout definition - Concrete implementation:
module Implementation : INTERFACE - Usage:
open Module - Encapsulation: Hide internal state
# Abstract Types and Interfaces
# Abstract type definition
module type ANIMAL = sig
type t
val create : string -> int -> t
val name : t -> string
val age : t -> int
val make_sound : t -> string
end
# Concrete implementation
module Dog : ANIMAL = struct
type t = { name: string; age: int }
let create name age = { name; age }
let name dog = dog.name
let age dog = dog.age
let make_sound _ = "Woof!"
end
module Cat : ANIMAL = struct
type t = { name: string; age: int }
let create name age = { name; age }
let name cat = cat.name
let age cat = cat.age
let make_sound _ = "Meow!"
end
# Using modules with same interface
let animal_example () =
let dog = Dog.create "Rex" 3 in
let cat = Cat.create "Whiskers" 2 in
Printf.printf "Dog: %s is %d years old\n" (Dog.name dog) (Dog.age dog);
Printf.printf "Cat: %s is %d years old\n" (Cat.name cat) (Cat.age cat);
Printf.printf "Dog says: %s\n" (Dog.make_sound dog);
Printf.printf "Cat says: %s\n" (Cat.make_sound cat)
# Polymorphic module
module type COLLECTION = sig
type 'a t
val empty : 'a t
val add : 'a -> 'a t -> 'a t
val mem : 'a -> 'a t -> bool
val fold : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'b
end
# Interface with abstract type
module type STACK = sig
type 'a t
val empty : 'a t
val push : 'a -> 'a t -> 'a t
val pop : 'a t -> 'a * 'a t
val peek : 'a t -> 'a
val is_empty : 'a t -> bool
end
# Implementing the stack interface
module ListStack : STACK = struct
type 'a t = 'a list
let empty = []
let push x s = x :: s
let pop = function
| [] -> failwith "Empty stack"
| h :: t -> (h, t)
let peek = function
| [] -> failwith "Empty stack"
| h :: _ -> h
let is_empty = function
| [] -> true
| _ -> false
end
let () =
animal_example ()Parameterized (polymorphic) types in OCaml allow for generic programming and type-safe abstractions.
- Definition:
type 'a option = None | Some of 'a - Multiple parameters:
type ('a, 'b) pair - Polymorphic functions:
let identity x = x - GADTs: Generalized Algebraic Data Types
- Functors: Modules parameterized by modules
# Parameterized Types (Polymorphic Types)
# Basic parameterized type
type 'a option =
| None
| Some of 'a
type ('a, 'b) pair = {
first: 'a;
second: 'b;
}
# Using parameterized types
let option_example () =
let x = Some 42 in
let y: int option = Some 100 in
let z: string option = Some "hello" in
match x with
| Some value -> Printf.printf "Value: %d\n" value
| None -> print_endline "None"
# Polymorphic functions
let identity x = x
let compose f g x = f (g x)
# Polymorphic list functions
let rec length = function
| [] -> 0
| _ :: t -> 1 + length t
let rec map f = function
| [] -> []
| h :: t -> f h :: map f t
# Type constraints
let add_ints (x: int) (y: int) = x + y
let add_floats (x: float) (y: float) = x +. y
# GADTs (Generalized Algebraic Data Types)
type _ expr =
| Int : int -> int expr
| Add : int expr * int expr -> int expr
| Bool : bool -> bool expr
| If : bool expr * 'a expr * 'a expr -> 'a expr
let rec eval : type a. a expr -> a = function
| Int i -> i
| Add (e1, e2) -> eval e1 + eval e2
| Bool b -> b
| If (cond, e1, e2) -> if eval cond then eval e1 else eval e2
# Higher-kinded polymorphism using modules
module type FUNCTOR = sig
type 'a t
val map : ('a -> 'b) -> 'a t -> 'b t
end
module ListFunctor : FUNCTOR = struct
type 'a t = 'a list
let map = List.map
end
let () =
option_example ();
Printf.printf "Length: %d\n" (length [1; 2; 3; 4; 5]);
let expr = Add (Int 2, Add (Int 3, Int 4)) in
Printf.printf "Eval: %d\n" (eval expr)OCaml metaprogramming uses PPX extensions and syntax extensions to generate code and add language features.
- ppx_deriving: Code generation
- ppx_jane: Jane Street's extensions
- Camlp4: Legacy syntax extensions
- Quotations:
<:expr< ... >>
# Macros and Metaprogramming in OCaml
# Using Camlp4 for syntax extensions
# (Requires: opam install camlp4)
# Example of a simple macro (Camlp4)
# (Save as macro.ml)
# open Camlp4.PreCast
#
# let add_log expr =
# <:expr< (fun x -> Printf.printf "Value: %d\n" x; $expr$) >>
# Using ppx_deriving for code generation
# (Requires: opam install ppx_deriving)
type person = {
name: string;
age: int;
} [@@deriving show, eq, ord]
let person_example () =
let alice = { name = "Alice"; age = 25 } in
Printf.printf "Person: %s\n" (show_person alice);
let bob = { name = "Bob"; age = 30 } in
Printf.printf "Equal: %b\n" (person_equal alice bob);
Printf.printf "Compare: %d\n" (person_compare alice bob)
# Using ppx_deriving for JSON serialization
type data = {
name: string;
age: int;
hobbies: string list;
} [@@deriving yojson]
let json_serialization () =
let data = { name = "Alice"; age = 25; hobbies = ["reading"; "coding"] } in
let json = data_to_yojson data in
let json_string = Yojson.Safe.to_string json in
Printf.printf "JSON: %s\n" json_string
# Using ppx_sexp for S-expressions
# (Requires: opam install ppx_sexp_conv)
open Sexplib
type person2 = {
name: string;
age: int;
} [@@deriving sexp]
let sexp_example () =
let alice = { name = "Alice"; age = 25 } in
let sexp = sexp_of_person2 alice in
Printf.printf "Sexp: %s\n" (Sexp.to_string sexp)
# Using ppx_compare for comparison
# (Requires: opam install ppx_compare)
type point = {
x: int;
y: int;
} [@@deriving compare]
let compare_example () =
let p1 = { x = 1; y = 2 } in
let p2 = { x = 3; y = 4 } in
Printf.printf "Compare: %d\n" (compare_point p1 p2)
# Using ppx_fields for record field access
# (Requires: opam install ppx_fields)
type record = {
field1: int;
field2: string;
} [@@deriving fields]
let fields_example () =
let r = { field1 = 42; field2 = "hello" } in
let field1 = Fields.field1_get r in
Printf.printf "Field1: %d\n" field1
let () =
person_example ();
json_serialization ();
sexp_example ();
compare_example ();
fields_example ()OCaml supports generators and coroutines through Lwt (lightweight threads), Async, and the Stream module.
- Lwt:
Lwt.return,Lwt.bind - Async:
Deferred.return - Stream:
Stream.next - Channels: Lwt_stream
- Cooperative: Lwt.join
# Generators and Coroutines in OCaml
# Using Lwt for coroutines
open Lwt
let coroutine_example () =
let generator () =
let state = ref 0 in
fun () ->
state := !state + 1;
!state
in
let next = generator () in
Printf.printf "%d\n" (next ());
Printf.printf "%d\n" (next ());
Printf.printf "%d\n" (next ())
# Using Lwt for async generators
let async_generator () =
let rec fib a b =
Lwt.return a >>= fun value ->
Lwt.return (fib b (a + b))
in
fib 0 1
# Using Stream module (built-in)
let stream_example () =
let rec numbers n =
[< 'n; numbers (n + 1) >]
in
let stream = numbers 1 in
for i = 1 to 10 do
match Stream.next stream with
| Some n -> Printf.printf "%d " n
| None -> ()
done;
print_endline ""
# Using Lwt for cooperative multitasking
let cooperative_example () =
let task1 () =
Lwt_unix.sleep 1.0 >>= fun () ->
Lwt_io.printf "Task 1 completed\n"
in
let task2 () =
Lwt_unix.sleep 2.0 >>= fun () ->
Lwt_io.printf "Task 2 completed\n"
in
let task3 () =
Lwt_unix.sleep 0.5 >>= fun () ->
Lwt_io.printf "Task 3 completed\n"
in
Lwt.join [task1 (); task2 (); task3 ()]
# Using Async for coroutines
open Async
let async_coroutine_example () =
let counter = ref 0 in
let generator () =
counter := !counter + 1;
Deferred.return !counter
in
Deferred.both
(generator ())
(Deferred.both
(generator ())
(generator ())) >>= fun (a, (b, c)) ->
Printf.printf "%d, %d, %d\n" a b c;
return ()
# Generator using Lwt_stream
let lwt_stream_example () =
let rec fib_stream a b =
Lwt_stream.of_list [a] >>= fun stream ->
Lwt_stream.append stream (fib_stream b (a + b))
in
let stream = fib_stream 0 1 in
Lwt_stream.iter (fun x -> Printf.printf "%d " x) stream
let () =
coroutine_example ();
Lwt_main.run (cooperative_example ());
stream_example ()OCaml provides array operations including initialization, reshaping, vector operations, and matrix arithmetic through Bigarray.
- Init:
Array.init 10 (fun i -> i) - Reshape: Manual using loops
- Vector ops: Element-wise operations
- Matrix multiplication:
mmult
# Advanced Array Operations
# Array initialization
let array_example () =
let arr1 = Array.init 10 (fun i -> i) in
let arr2 = Array.make 10 0 in
let arr3 = Array.create 10 5 in
# Printing arrays
Array.iter (fun x -> Printf.printf "%d " x) arr1;
print_endline "";
# Reshaping (simulating using slicing)
let reshape arr rows cols =
let result = Array.make_matrix rows cols 0 in
for i = 0 to rows - 1 do
for j = 0 to cols - 1 do
result.(i).(j) <- arr.(i * cols + j)
done
done;
result
in
let matrix = reshape arr1 2 5 in
Array.iter (fun row ->
Array.iter (fun x -> Printf.printf "%d " x) row;
print_endline ""
) matrix
# Vector operations
let vector_ops () =
let v1 = [|1; 2; 3; 4; 5|] in
let v2 = [|6; 7; 8; 9; 10|] in
# Element-wise addition
let v3 = Array.init 5 (fun i -> v1.(i) + v2.(i)) in
# Dot product
let dot = Array.fold_left (+) 0 (Array.init 5 (fun i -> v1.(i) * v2.(i))) in
# Scalar multiplication
let v4 = Array.map (fun x -> x * 2) v1 in
Printf.printf "Dot product: %d\n" dot;
Array.iter (fun x -> Printf.printf "%d " x) v4;
print_endline ""
# Matrix operations
let matrix_ops () =
let m1 = [| [|1; 2|]; [|3; 4|] |] in
let m2 = [| [|5; 6|]; [|7; 8|] |] in
# Matrix addition
let m3 = Array.init 2 (fun i ->
Array.init 2 (fun j -> m1.(i).(j) + m2.(i).(j))
) in
# Matrix multiplication
let mmult a b =
let n = Array.length a in
let m = Array.length b.(0) in
let p = Array.length b in
let result = Array.make_matrix n m 0 in
for i = 0 to n - 1 do
for j = 0 to m - 1 do
let sum = ref 0 in
for k = 0 to p - 1 do
sum := !sum + a.(i).(k) * b.(k).(j)
done;
result.(i).(j) <- !sum
done
done;
result
in
let m4 = mmult m1 m2 in
Array.iter (fun row ->
Array.iter (fun x -> Printf.printf "%d " x) row;
print_endline ""
) m4
# Using Bigarray for large arrays
open Bigarray
let bigarray_example () =
let arr = Array1.create int C_layout 100 in
for i = 0 to 99 do
arr.{i} <- i
done;
Printf.printf "Bigarray sum: %d\n" (Array1.fold_left (+) 0 arr)
let () =
array_example ();
vector_ops ();
matrix_ops ();
bigarray_example ()Missing data in OCaml is handled using the option type (None/Some) or the result type for error handling.
- Option:
type 'a option = None | Some of 'a - Result:
type ('a, 'b) result = Ok of 'a | Error of 'b - Filter:
List.filter_map - Default:
Option.value ~default:0 - Or_error:
Or_error.return
# Working with Missing Data (Option type)
# Using Option type for missing data
open Option
let option_example () =
# Creating values with missing data
let data = [Some 1; Some 2; None; Some 4; Some 5; None; Some 7] in
# Check for missing values
let has_missing = List.exists Option.is_none data in
Printf.printf "Has missing: %b\n" has_missing
# Remove missing values
let clean_data = List.filter_map identity data in
List.iter (fun x -> Printf.printf "%d " x) clean_data;
print_endline ""
# Replace missing values
let replaced = List.map (Option.value ~default:0) data in
List.iter (fun x -> Printf.printf "%d " x) replaced;
print_endline ""
# Operations with missing values
let x = [Some 1; Some 2; None; Some 4] in
let y = [Some 5; Some 6; None; Some 8] in
let z = List.map2 (fun a b ->
match a, b with
| Some a, Some b -> Some (a + b)
| _, _ -> None
) x y in
List.iter (fun x ->
match x with
| Some v -> Printf.printf "%d " v
| None -> print_string "None "
) z;
print_endline ""
# Ignoring missing values
let sum_complete = List.fold_left (+)
(List.fold_left (fun acc x -> match x with Some v -> acc + v | None -> acc) 0 x)
0 in
Printf.printf "Sum of complete data: %d\n" sum_complete
# Using Result type for error handling
let result_example () =
let divide a b =
if b = 0 then Error "Division by zero"
else Ok (a / b)
in
let result1 = divide 10 2 in
let result2 = divide 10 0 in
match result1 with
| Ok v -> Printf.printf "10/2 = %d\n" v
| Error e -> Printf.printf "Error: %s\n" e;
match result2 with
| Ok v -> Printf.printf "10/0 = %d\n" v
| Error e -> Printf.printf "Error: %s\n" e
# Using Or_error from Core
open Core
let or_error_example () =
let divide a b =
if b = 0 then Or_error.error "Division by zero" ()
else Or_error.return (a / b)
in
let result = divide 10 2 in
match result with
| Ok v -> Printf.printf "Result: %d\n" v
| Error e -> Printf.printf "Error: %s\n" (Error.to_string e)
let () =
option_example ();
result_example ()OCaml provides sorting and searching functions through the List module and custom implementations for binary search.
- Sort:
List.sort compare - Custom comparator:
List.sort (fun a b -> ...) - Search:
List.filter,List.find_opt - Binary search: Implement manually
- Membership:
List.mem
# Sorting and Searching
# Basic sorting
let sort_example () =
let arr = [5; 2; 8; 1; 9; 3] in
let sorted = List.sort Int.compare arr in
List.iter (fun x -> Printf.printf "%d " x) sorted;
print_endline ""
# Sorting without mutation (List.sort creates new list)
let arr2 = [5; 2; 8; 1; 9; 3] in
let sorted2 = List.sort Int.compare arr2 in
Printf.printf "Original: ";
List.iter (fun x -> Printf.printf "%d " x) arr2;
print_endline "";
Printf.printf "Sorted: ";
List.iter (fun x -> Printf.printf "%d " x) sorted2;
print_endline ""
# Sorting with custom comparator
let pairs = [(5, "apple"); (3, "banana"); (8, "cherry")] in
let sorted_pairs = List.sort (fun (x, _) (y, _) -> Int.compare x y) pairs in
List.iter (fun (n, s) -> Printf.printf "(%d, %s) " n s) sorted_pairs;
print_endline ""
# Sorting descending
let arr3 = [5; 2; 8; 1; 9; 3] in
let sorted_desc = List.sort (fun x y -> Int.compare y x) arr3 in
List.iter (fun x -> Printf.printf "%d " x) sorted_desc;
print_endline ""
# Search functions
let arr4 = [1; 3; 5; 7; 9; 11] in
let greater_than_5 = List.filter (fun x -> x > 5) arr4 in
let first_greater_than_5 = List.find_opt (fun x -> x > 5) arr4 in
let last_greater_than_5 =
List.fold_left (fun acc x -> if x > 5 then Some x else acc) None arr4
in
Printf.printf "Greater than 5: ";
List.iter (fun x -> Printf.printf "%d " x) greater_than_5;
print_endline "";
match first_greater_than_5 with
| Some v -> Printf.printf "First greater: %d\n" v
| None -> print_endline "None";
match last_greater_than_5 with
| Some v -> Printf.printf "Last greater: %d\n" v
| None -> print_endline "None"
# Contains
let has_seven = List.mem 7 arr4 in
let has_four = List.mem 4 arr4 in
Printf.printf "Has 7: %b\n" has_seven;
Printf.printf "Has 4: %b\n" has_four
# Binary search on array
let binary_search arr target =
let rec search low high =
if low > high then None
else
let mid = (low + high) / 2 in
let mid_val = arr.(mid) in
if mid_val = target then Some mid
else if mid_val < target then search (mid + 1) high
else search low (mid - 1)
in
search 0 (Array.length arr - 1)
let () =
sort_example ();
let arr = [|1; 2; 3; 4; 5; 6; 7|] in
match binary_search arr 5 with
| Some idx -> Printf.printf "Found at index: %d\n" idx
| None -> print_endline "Not found"OCaml provides mathematical operations through the standard library, Owl for scientific computing, and complex number support.
- Owl:
Special.gamma - Random:
Random.int,Random.float - Complex:
Complex.{ re; im }
# Mathematical Operations
# Basic arithmetic
let math_example () =
let x = 10 in
let y = 3 in
Printf.printf "x + y = %d\n" (x + y);
Printf.printf "x - y = %d\n" (x - y);
Printf.printf "x * y = %d\n" (x * y);
Printf.printf "x / y = %d\n" (x / y);
Printf.printf "x mod y = %d\n" (x mod y)
# Mathematical functions (float)
let pi = 4.0 *. atan 1.0 in
Printf.printf "sin(pi/4) = %f\n" (sin (pi /. 4.0));
Printf.printf "cos(pi/4) = %f\n" (cos (pi /. 4.0));
Printf.printf "tan(pi/4) = %f\n" (tan (pi /. 4.0));
Printf.printf "exp(1) = %f\n" (exp 1.0);
Printf.printf "log(e) = %f\n" (log (exp 1.0));
Printf.printf "log10(100) = %f\n" (log10 100.0);
Printf.printf "sqrt(9) = %f\n" (sqrt 9.0)
# Special functions (using Batteries or Owl)
open Owl
let special_functions () =
Printf.printf "Gamma(5) = %f\n" (Special.gamma 5.0);
Printf.printf "Beta(2,3) = %f\n" (Special.beta 2.0 3.0)
# Random numbers
let random_example () =
Random.self_init ();
Printf.printf "Random int: %d\n" (Random.int 10);
Printf.printf "Random float: %f\n" (Random.float 1.0);
Printf.printf "Random bool: %b\n" (Random.bool ());
# Random list
let random_list = List.init 10 (fun _ -> Random.int 100) in
List.iter (fun x -> Printf.printf "%d " x) random_list;
print_endline ""
# Statistics (using Owl)
let statistics_example () =
let data = Array.init 1000 (fun _ -> Random.float 1.0) in
let mean = Stats.mean data in
let std = Stats.std data in
let var = Stats.var data in
Printf.printf "Mean: %f\n" mean;
Printf.printf "Std Dev: %f\n" std;
Printf.printf "Variance: %f\n" var
# Complex numbers (using Owl)
let complex_example () =
let c1 = Complex.{ re = 1.0; im = 2.0 } in
let c2 = Complex.{ re = 3.0; im = 4.0 } in
let c3 = Complex.add c1 c2 in
let c4 = Complex.mul c1 c2 in
Printf.printf "c1 + c2 = %f + %fi\n" c3.re c3.im;
Printf.printf "c1 * c2 = %f + %fi\n" c4.re c4.im
let () =
math_example ();
random_example ();
statistics_example ();
complex_example ()OCaml provides data serialization through Sexplib (S-expressions), Yojson (JSON), Marshal (binary), and Bin_prot.
- Sexplib:
sexp_of_type,type_of_sexp - Yojson:
person_to_yojson - Marshal:
Marshal.to_string - Bin_prot:
bin_prot_of_type - Custom: Manual serialization
# Data Serialization
# Using Sexplib for S-expressions
# (Requires: opam install sexplib)
open Sexplib
type data = {
name: string;
age: int;
hobbies: string list;
} [@@deriving sexp]
let sexp_example () =
let d = { name = "Alice"; age = 25; hobbies = ["reading"; "coding"] } in
let sexp = sexp_of_data d in
let serialized = Sexp.to_string sexp in
Printf.printf "Serialized: %s\n" serialized;
# Deserialize
let parsed = Sexp.of_string serialized in
let deserialized = data_of_sexp parsed in
Printf.printf "Deserialized: %s, %d\n" deserialized.name deserialized.age
# Using Yojson for JSON serialization
open Yojson
type person = {
name: string;
age: int;
city: string option;
} [@@deriving yojson]
let yojson_example () =
let p = { name = "Alice"; age = 25; city = Some "NYC" } in
let json = person_to_yojson p in
let json_string = Yojson.Safe.to_string json in
Printf.printf "JSON: %s\n" json_string;
# Deserialize
let parsed = Yojson.Safe.from_string json_string in
let person = person_of_yojson parsed in
match person with
| Ok p -> Printf.printf "Parsed: %s, %d\n" p.name p.age
| Error e -> Printf.printf "Error: %s\n" e
# Using Marshal for binary serialization
let marshal_example () =
let data = (42, "hello", [1; 2; 3]) in
let serialized = Marshal.to_string data [] in
Printf.printf "Serialized length: %d\n" (String.length serialized);
let deserialized = Marshal.from_string serialized 0 in
let (num, str, lst) = deserialized in
Printf.printf "Deserialized: %d, %s\n" num str
# Using Bin_prot for binary serialization
# (Requires: opam install bin_prot)
open Core
type bin_data = {
id: int;
name: string;
values: int list;
} [@@deriving bin_io]
let bin_prot_example () =
let d = { id = 1; name = "Alice"; values = [1; 2; 3] } in
let bin = bin_data_to_bin_prot d in
Printf.printf "Binary length: %d\n" (String.length bin);
let deserialized = bin_data_of_bin_prot bin in
Printf.printf "Deserialized: %d, %s\n" deserialized.id deserialized.name
let () =
sexp_example ();
yojson_example ();
marshal_example ();
bin_prot_example ()OCaml provides C interfaces through Ctypes for dynamic linking and C stubs for static linking with foreign function interface.
- Ctypes:
foreign,Dl.dlopen - External:
external add: int -> int -> int = "caml_add" - C stub:
CAMLprim value caml_add - Headers:
caml/mlvalues.h - Memory:
CAMLparam,CAMLreturn
# Interfacing with C
# Using Ctypes library
# (Requires: opam install ctypes)
open Ctypes
open Foreign
let ctypes_example () =
# Load C library
let lib = Dl.dlopen ~filename:"libc.so.6" ~flags:[Dl.RTLD_LAZY] in
# Define C functions
let strlen = foreign "strlen" ~from:lib (string @-> returning size_t) in
let strcmp = foreign "strcmp" ~from:lib (string @-> string @-> returning int) in
# Call C functions
let length = strlen "Hello, World!" in
let cmp = strcmp "hello" "hello" in
Printf.printf "Length: %lu\n" length;
Printf.printf "Compare: %d\n" cmp
# Using C stubs
# (Create C file: mylib.c)
# int add(int a, int b) { return a + b; }
# int multiply(int a, int b) { return a * b; }
# (OCaml file with external declarations)
# (Compile with: ocamlc -custom mylib.o mylib.ml)
external add: int -> int -> int = "caml_add"
external multiply: int -> int -> int = "caml_multiply"
let stub_example () =
let sum = add 5 3 in
let product = multiply 4 5 in
Printf.printf "5 + 3 = %d\n" sum;
Printf.printf "4 * 5 = %d\n" product
# Example C stub code
# (Save as mylib.c)
# #include <caml/mlvalues.h>
# #include <caml/alloc.h>
# #include <caml/memory.h>
# #include <caml/callback.h>
#
# CAMLprim value caml_add(value a, value b) {
# CAMLparam2(a, b);
# CAMLreturn(Val_int(Int_val(a) + Int_val(b)));
# }
#
# CAMLprim value caml_multiply(value a, value b) {
# CAMLparam2(a, b);
# CAMLreturn(Val_int(Int_val(a) * Int_val(b)));
# }
# Using C structs with Ctypes
let c_struct_example () =
type point = {
x: int;
y: int;
} [@@deriving cstruct]
let lib = Dl.dlopen ~filename:"libm.so.6" ~flags:[Dl.RTLD_LAZY] in
let sin = foreign "sin" ~from:lib (double @-> returning double) in
let result = sin 0.5 in
Printf.printf "sin(0.5) = %f\n" result
let () =
ctypes_example ();
stub_example ();
c_struct_example ()Reverse a string using a loop, recursion, or fold. Strings are immutable, so build a new string.
- Loop:
String.create len - Recursion:
helper acc idx - Fold:
String.fold_left - Performance: O(n) time
# Reverse a string
let reverse_string s =
let len = String.length s in
let reversed = String.create len in
for i = 0 to len - 1 do
reversed.[i] <- s.[len - 1 - i]
done;
reversed
let reverse_string_rec s =
let rec helper acc idx =
if idx < 0 then acc
else helper (acc ^ String.make 1 s.[idx]) (idx - 1)
in
helper "" (String.length s - 1)
let reverse_string_fold s =
String.fold_left (fun acc c -> String.make 1 c ^ acc) "" s
let () =
let s = "hello" in
Printf.printf "Original: %s\n" s;
Printf.printf "Reversed: %s\n" (reverse_string s);
Printf.printf "Reversed (rec): %s\n" (reverse_string_rec s);
Printf.printf "Reversed (fold): %s\n" (reverse_string_fold s)Check if a string is a palindrome by comparing characters from both ends.
- Two-pointer: Compare from ends
- Case insensitive:
String.lowercase_ascii - Ignore spaces:
String.filter - Fold: Compare to reversed
# Check palindrome
let is_palindrome s =
let len = String.length s in
let rec check i =
if i >= len / 2 then true
else if s.[i] = s.[len - 1 - i] then check (i + 1)
else false
in
check 0
let is_palindrome_case_insensitive s =
let lower = String.lowercase_ascii s in
let len = String.length lower in
let rec check i =
if i >= len / 2 then true
else if lower.[i] = lower.[len - 1 - i] then check (i + 1)
else false
in
check 0
let is_palindrome_with_spaces s =
let cleaned = String.filter (fun c -> c <> ' ') s in
is_palindrome cleaned
let is_palindrome_fold s =
let reversed = String.fold_left (fun acc c -> String.make 1 c ^ acc) "" s in
s = reversed
let () =
let test_strings = ["racecar"; "hello"; "A man a plan a canal Panama"; "race a car"] in
List.iter (fun s ->
Printf.printf ""%s" is palindrome: %b\n" s (is_palindrome_case_insensitive s)
) test_stringsFind the maximum value in an array using iteration, recursion, or fold.
- Iteration:
Array.fold_left max - Recursion:
helper max_idx idx - Built-in:
Array.fold_left max arr.(0) - Option: Handle empty arrays
# Find max in array
let find_max arr =
let max = ref arr.(0) in
for i = 1 to Array.length arr - 1 do
if arr.(i) > !max then max := arr.(i)
done;
!max
let find_max_rec arr =
let rec helper max_idx idx =
if idx >= Array.length arr then max_idx
else if arr.(idx) > arr.(max_idx) then helper idx (idx + 1)
else helper max_idx (idx + 1)
in
arr.(helper 0 1)
let find_max_fold arr =
Array.fold_left max arr.(0) arr
let find_max_option arr =
if Array.length arr = 0 then None
else Some (Array.fold_left max arr.(0) arr)
let () =
let arr = [|1; 5; 3; 9; 2|] in
Printf.printf "Array: ";
Array.iter (fun x -> Printf.printf "%d " x) arr;
print_endline "";
Printf.printf "Max: %d\n" (find_max arr);
Printf.printf "Max (rec): %d\n" (find_max_rec arr);
Printf.printf "Max (fold): %d\n" (find_max_fold arr);
match find_max_option arr with
| Some v -> Printf.printf "Max (option): %d\n" v
| None -> print_endline "Empty array"Remove duplicate elements from a list using fold and membership test, or using sets.
- Fold:
List.fold_left - Set: Convert to set and back
- Recursive: Helper with seen list
- Preserve order:
List.rev
# Remove duplicates
let remove_duplicates lst =
let rec helper seen result = function
| [] -> List.rev result
| h :: t ->
if List.mem h seen then helper seen result t
else helper (h :: seen) (h :: result) t
in
helper [] [] lst
let remove_duplicates_set lst =
let set = List.fold_left (fun acc x -> StringSet.add x acc) StringSet.empty lst in
StringSet.to_list set
let remove_duplicates_fold lst =
List.fold_left (fun acc x ->
if List.mem x acc then acc
else x :: acc
) [] lst
|> List.rev
let () =
let lst = ["apple"; "banana"; "apple"; "orange"; "banana"; "grape"] in
Printf.printf "Original: ";
List.iter (fun x -> Printf.printf "%s " x) lst;
print_endline "";
Printf.printf "Without duplicates: ";
List.iter (fun x -> Printf.printf "%s " x) (remove_duplicates lst);
print_endline "";
Printf.printf "Without duplicates (fold): ";
List.iter (fun x -> Printf.printf "%s " x) (remove_duplicates_fold lst);
print_endline ""Merge arrays using array append or list concatenation. Merge sorted arrays efficiently.
- Array:
Array.append - List:
@operator - Sorted merge:
merge acc l1 l2 - Unique: Merge and remove duplicates
# Merge arrays
let merge_arrays arr1 arr2 =
Array.append arr1 arr2
let merge_lists lst1 lst2 =
lst1 @ lst2
let merge_unique lst1 lst2 =
let combined = lst1 @ lst2 in
remove_duplicates combined
let merge_sorted lst1 lst2 =
let rec merge acc l1 l2 =
match l1, l2 with
| [], [] -> List.rev acc
| [], l2 -> List.rev (List.rev_append acc l2)
| l1, [] -> List.rev (List.rev_append acc l1)
| h1 :: t1, h2 :: t2 ->
if h1 <= h2 then merge (h1 :: acc) t1 l2
else merge (h2 :: acc) l1 t2
in
merge [] lst1 lst2
let () =
let arr1 = [|1; 2; 3|] in
let arr2 = [|4; 5; 6|] in
let merged = merge_arrays arr1 arr2 in
Printf.printf "Merged arrays: ";
Array.iter (fun x -> Printf.printf "%d " x) merged;
print_endline "";
let lst1 = [1; 2; 3] in
let lst2 = [4; 5; 6] in
let merged_lst = merge_lists lst1 lst2 in
Printf.printf "Merged lists: ";
List.iter (fun x -> Printf.printf "%d " x) merged_lst;
print_endline "";
let sorted1 = [1; 3; 5; 7] in
let sorted2 = [2; 4; 6; 8] in
let merged_sorted = merge_sorted sorted1 sorted2 in
Printf.printf "Merged sorted: ";
List.iter (fun x -> Printf.printf "%d " x) merged_sorted;
print_endline ""Convert a string to a number using float_of_string or int_of_string with error handling.
- Float:
float_of_string - Int:
int_of_string - Safe:
float_of_string_opt - Error handling:
try
# Convert string to number
let string_to_float s =
try float_of_string s
with Failure _ -> 0.0
let string_to_int s =
try int_of_string s
with Failure _ -> 0
let string_to_float_safe s =
match float_of_string_opt s with
| Some v -> v
| None -> 0.0
let string_to_int_safe s =
match int_of_string_opt s with
| Some v -> v
| None -> 0
let string_to_number s =
try
if String.contains s '.' then
Some (float_of_string s)
else
Some (float_of_string s)
with Failure _ -> None
let () =
let strings = ["42"; "3.14"; "hello"; "123"; "45.67"] in
List.iter (fun s ->
Printf.printf ""%s" -> int: %d, float: %f\n" s (string_to_int s) (string_to_float s)
) stringsIterate through an association list using List.iter or List.map.
- Iter:
List.iter (fun (k, v) -> ...) - Map:
List.mapfor transformation - Find:
List.assoc - Fold:
List.fold_left
# Loop through dictionary (association list)
let loop_dict dict =
List.iter (fun (key, value) ->
Printf.printf "%s => %s\n" key value
) dict
let loop_dict_map dict =
List.map (fun (key, value) ->
Printf.printf "%s => %s\n" key value;
(key, value)
) dict
let find_key dict key =
try Some (List.assoc key dict)
with Not_found -> None
let () =
let data = [("name", "Alice"); ("age", "25"); ("city", "NYC")] in
Printf.printf "Dictionary:\n";
loop_dict data;
Printf.printf "\n";
match find_key data "name" with
| Some v -> Printf.printf "Name: %s\n" v
| None -> print_endline "Name not found";
match find_key data "country" with
| Some v -> Printf.printf "Country: %s\n" v
| None -> print_endline "Country not found"Delay function execution using Unix.sleep for blocking or Thread.delay for non-blocking.
- Blocking:
Unix.sleep seconds - Non-blocking:
Thread.delay seconds - Callback:
delay_with_callback - Async:
Lwt_unix.sleep
# Delay function execution
let delay_seconds seconds f =
Unix.sleep seconds;
f ()
let delay_async seconds f =
let _ = Thread.create (fun () ->
Thread.delay seconds;
f ()
) () in
()
let delay_with_callback seconds callback f =
let _ = Thread.create (fun () ->
Thread.delay seconds;
callback (f ())
) () in
()
let delayed_print message seconds =
Printf.printf "Starting delay of %d seconds\n" seconds;
delay_seconds seconds (fun () ->
Printf.printf "%s\n" message
)
let () =
Printf.printf "Delayed execution examples:\n";
delay_seconds 2 (fun () -> Printf.printf "After 2 seconds\n");
let callback result = Printf.printf "Callback: %d\n" result in
delay_with_callback 1 callback (fun () -> 42);
Thread.delay 3;
Printf.printf "Main thread continues\n"Make an HTTP GET request using Cohttp-lwt-unix or other HTTP clients.
- GET:
Cohttp_lwt_unix.Client.get - POST:
Client.post - Headers:
Header.init - JSON:
Cohttp_lwt_body.of_string
# HTTP GET request
# Using Cohttp
# (Requires: opam install cohttp-lwt-unix)
open Lwt
open Cohttp
open Cohttp_lwt_unix
let fetch_data url =
let uri = Uri.of_string url in
Client.get uri >>= fun (response, body) ->
Body.to_string body >>= fun body_text ->
Lwt.return body_text
let fetch_data_sync url =
Lwt_main.run (fetch_data url)
let post_data url data =
let uri = Uri.of_string url in
let body = Cohttp_lwt_body.of_string data in
Client.post uri ~body >>= fun (response, body) ->
Body.to_string body >>= fun body_text ->
Lwt.return body_text
let post_json url json_data =
let uri = Uri.of_string url in
let headers = Header.init_with "Content-Type" "application/json" in
let body = Cohttp_lwt_body.of_string json_data in
Client.post uri ~headers ~body >>= fun (response, body) ->
Body.to_string body >>= fun body_text ->
Lwt.return body_text
let () =
Printf.printf "HTTP examples available with Cohttp\n";
(* Example: *)
(* let result = fetch_data_sync "https://api.github.com" in *)
(* Printf.printf "%s\n" result *)Create a promise-like task using Lwt or Async for asynchronous computation.
- Lwt:
Lwt.task,Lwt.wakeup - Async:
Deferred.create - Resolve:
Lwt.wakeup resolver - Reject:
Lwt.wakeup_exn
# Create a promise-like task
open Lwt
let create_promise should_resolve =
Lwt.task ()
let create_promise_with_delay delay should_resolve =
let promise, resolver = Lwt.task () in
let _ = Lwt.async (fun () ->
Lwt_unix.sleep delay >>= fun () ->
if should_resolve then
Lwt.wakeup resolver "Success!"
else
Lwt.wakeup_exn resolver (Failure "Failed!");
Lwt.return ()
) in
promise
let chain_promises p1 p2 =
p1 >>= fun result1 ->
Printf.printf "First: %s\n" result1;
p2 >>= fun result2 ->
Printf.printf "Second: %s\n" result2;
Lwt.return (result1, result2)
let () =
let promise1 = create_promise_with_delay 1.0 true in
let promise2 = create_promise_with_delay 2.0 true in
Lwt_main.run (
chain_promises promise1 promise2 >>= fun (r1, r2) ->
Printf.printf "Both completed: %s, %s\n" r1 r2;
Lwt.return ()
);
let failed_promise = create_promise_with_delay 1.0 false in
Lwt_main.run (
Lwt.catch
(fun () -> failed_promise >>= Lwt.return)
(fun ex -> Printf.printf "Caught error: %s\n" (Printexc.to_string ex); Lwt.return ())
)Calculate factorial using recursion, iteration, or tail recursion.
- Recursive:
if n <= 1 then 1 else n * factorial (n-1) - Iterative:
for i = 2 to n do result := !result * i - Tail recursive:
helper acc n - Fold:
List.fold_left (*) 1
# Factorial
let rec factorial n =
if n <= 1 then 1
else n * factorial (n - 1)
let factorial_iter n =
let result = ref 1 in
for i = 2 to n do
result := !result * i
done;
!result
let factorial_tail n =
let rec helper acc n =
if n <= 1 then acc
else helper (acc * n) (n - 1)
in
helper 1 n
let factorial_fold n =
List.fold_left (*) 1 (List.init n (fun i -> i + 1))
let () =
let n = 5 in
Printf.printf "Factorial of %d:\n" n;
Printf.printf "Recursive: %d\n" (factorial n);
Printf.printf "Iterative: %d\n" (factorial_iter n);
Printf.printf "Tail recursive: %d\n" (factorial_tail n);
Printf.printf "Fold: %d\n" (factorial_fold n)Calculate Fibonacci numbers using recursion, iteration, or memoization.
- Recursive:
fib (n-1) + fib (n-2) - Iterative:
for i = 2 to n do a, b = b, a + b - Tail recursive:
helper a b i - Memoized:
Hashtbl.find
# Fibonacci
let rec fibonacci n =
if n <= 1 then n
else fibonacci (n - 1) + fibonacci (n - 2)
let fibonacci_iter n =
if n <= 1 then n
else
let a = ref 0 in
let b = ref 1 in
for i = 2 to n do
let c = !a + !b in
a := !b;
b := c
done;
!b
let fibonacci_tail n =
let rec helper a b i =
if i >= n then b
else helper b (a + b) (i + 1)
in
if n <= 1 then n
else helper 0 1 1
let fibonacci_memo n =
let cache = Hashtbl.create 100 in
let rec fib n =
if n <= 1 then n
else
try Hashtbl.find cache n
with Not_found ->
let result = fib (n - 1) + fib (n - 2) in
Hashtbl.add cache n result;
result
in
fib n
let () =
let n = 10 in
Printf.printf "Fibonacci of %d:\n" n;
Printf.printf "Recursive: %d\n" (fibonacci n);
Printf.printf "Iterative: %d\n" (fibonacci_iter n);
Printf.printf "Tail recursive: %d\n" (fibonacci_tail n);
Printf.printf "Memoized: %d\n" (fibonacci_memo n)Implement FizzBuzz using pattern matching or if-else conditions.
- If-else:
if i mod 15 = 0 ... - Pattern matching:
match (i mod 3, i mod 5) with - List:
List.init n - Output:
print_endline
# FizzBuzz
let fizzbuzz n =
for i = 1 to n do
if i mod 15 = 0 then print_endline "FizzBuzz"
else if i mod 3 = 0 then print_endline "Fizz"
else if i mod 5 = 0 then print_endline "Buzz"
else Printf.printf "%d\n" i
done
let fizzbuzz_list n =
List.init n (fun i -> i + 1)
|> List.map (fun i ->
if i mod 15 = 0 then "FizzBuzz"
else if i mod 3 = 0 then "Fizz"
else if i mod 5 = 0 then "Buzz"
else string_of_int i
)
let fizzbuzz_pattern n =
let rec helper i =
if i > n then ()
else
let result = match (i mod 3, i mod 5) with
| (0, 0) -> "FizzBuzz"
| (0, _) -> "Fizz"
| (_, 0) -> "Buzz"
| _ -> string_of_int i
in
print_endline result;
helper (i + 1)
in
helper 1
let () =
Printf.printf "FizzBuzz for 15:\n";
fizzbuzz 15;
Printf.printf "FizzBuzz list:\n";
List.iter print_endline (fizzbuzz_list 15);
Printf.printf "FizzBuzz pattern:\n";
fizzbuzz_pattern 15Find missing number using sum formula or XOR operation.
- Sum:
total - sum - XOR:
xor_all lxor xor_arr - Formula:
n * (n + 1) / 2 - Time: O(n)
# Find missing number
let find_missing arr =
let n = Array.length arr + 1 in
let total = n * (n + 1) / 2 in
let sum = Array.fold_left (+) 0 arr in
total - sum
let find_missing_list lst =
let n = List.length lst + 1 in
let total = n * (n + 1) / 2 in
let sum = List.fold_left (+) 0 lst in
total - sum
let find_missing_xor arr =
let n = Array.length arr + 1 in
let xor_all = ref 0 in
for i = 1 to n do
xor_all := !xor_all lxor i
done;
let xor_arr = ref 0 in
Array.iter (fun x -> xor_arr := !xor_arr lxor x) arr;
!xor_all lxor !xor_arr
let () =
let arr = [|1; 2; 4; 5; 6|] in
Printf.printf "Missing number: %d\n" (find_missing arr);
Printf.printf "Missing number (XOR): %d\n" (find_missing_xor arr);
let lst = [1; 2; 4; 5; 6] in
Printf.printf "Missing number (list): %d\n" (find_missing_list lst)Find duplicate elements using a hashtable or sorting the list.
- Hashtbl:
Hashtbl.mem - Sorting:
List.sort compare - Set:
StringSet.add - Complexity: O(n) or O(n log n)
# Find duplicates
let find_duplicates lst =
let seen = Hashtbl.create 10 in
let duplicates = ref [] in
List.iter (fun x ->
if Hashtbl.mem seen x then
duplicates := x :: !duplicates
else
Hashtbl.add seen x ()
) lst;
!duplicates
let find_duplicates_unique lst =
let seen = Hashtbl.create 10 in
let duplicates = Hashtbl.create 10 in
List.iter (fun x ->
if Hashtbl.mem seen x then
Hashtbl.add duplicates x ()
else
Hashtbl.add seen x ()
) lst;
Hashtbl.fold (fun key _ acc -> key :: acc) duplicates []
let find_duplicates_sorted lst =
let sorted = List.sort compare lst in
let rec helper acc prev = function
| [] -> acc
| h :: t ->
if h = prev then helper (h :: acc) h t
else helper acc h t
in
match sorted with
| [] -> []
| h :: t -> helper [] h t
|> List.rev
let () =
let lst = [1; 2; 3; 2; 4; 3; 5; 6; 5] in
Printf.printf "Original: ";
List.iter (fun x -> Printf.printf "%d " x) lst;
print_endline "";
Printf.printf "Duplicates: ";
List.iter (fun x -> Printf.printf "%d " x) (find_duplicates lst);
print_endline "";
Printf.printf "Unique duplicates: ";
List.iter (fun x -> Printf.printf "%d " x) (find_duplicates_unique lst);
print_endline "";
Printf.printf "Sorted duplicates: ";
List.iter (fun x -> Printf.printf "%d " x) (find_duplicates_sorted lst);
print_endline ""Sum array elements using fold, recursion, or iteration.
- Fold:
Array.fold_left (+) 0 - Recursive:
helper idx acc - Iterative:
for i = 0 to n-1 - List:
List.fold_left (+) 0
# Sum of array
let sum_array arr =
Array.fold_left (+) 0 arr
let sum_array_rec arr =
let rec helper idx acc =
if idx >= Array.length arr then acc
else helper (idx + 1) (acc + arr.(idx))
in
helper 0 0
let sum_array_iter arr =
let sum = ref 0 in
for i = 0 to Array.length arr - 1 do
sum := !sum + arr.(i)
done;
!sum
let sum_list lst =
List.fold_left (+) 0 lst
let () =
let arr = [|1; 2; 3; 4; 5|] in
let lst = [1; 2; 3; 4; 5] in
Printf.printf "Array: ";
Array.iter (fun x -> Printf.printf "%d " x) arr;
print_endline "";
Printf.printf "Sum (fold): %d\n" (sum_array arr);
Printf.printf "Sum (rec): %d\n" (sum_array_rec arr);
Printf.printf "Sum (iter): %d\n" (sum_array_iter arr);
Printf.printf "Sum (list): %d\n" (sum_list lst)Calculate average by dividing sum by length. Handle empty arrays.
- Float:
float_of_int sum /. float_of_int n - Integer:
sum / n - Empty: Return 0.0
- Precision: Use floats for division
# Average of array
let average_array arr =
if Array.length arr = 0 then 0.0
else float_of_int (sum_array arr) /. float_of_int (Array.length arr)
let average_array_float arr =
if Array.length arr = 0 then 0.0
else Array.fold_left (+.) 0.0 arr /. float_of_int (Array.length arr)
let average_list lst =
if List.length lst = 0 then 0.0
else float_of_int (sum_list lst) /. float_of_int (List.length lst)
let average_integer arr =
if Array.length arr = 0 then 0
else sum_array arr / Array.length arr
let () =
let int_arr = [|1; 2; 3; 4; 5|] in
let float_arr = [|1.0; 2.0; 3.0; 4.0; 5.0|] in
let lst = [1; 2; 3; 4; 5] in
Printf.printf "Average (int array): %f\n" (average_array int_arr);
Printf.printf "Average (float array): %f\n" (average_array_float float_arr);
Printf.printf "Average (list): %f\n" (average_list lst);
Printf.printf "Average (integer): %d\n" (average_integer int_arr)Sort arrays using Array.sort or List.sort with compare function.
- Array:
Array.sort compare arr - List:
List.sort compare lst - Custom:
List.sort (fun a b -> ...) - In-place:
Array.sortmutates
# Sort array ascending
let sort_array arr =
Array.sort compare arr;
arr
let sort_list lst =
List.sort compare lst
let sort_list_custom lst cmp =
List.sort cmp lst
let sort_by_key lst key =
List.sort (fun a b -> compare (key a) (key b)) lst
let sort_with_custom lst =
List.sort (fun a b ->
match compare a b with
| 0 -> 0
| -1 -> -1
| _ -> 1
) lst
let () =
let arr = [|5; 2; 8; 1; 9; 3|] in
let lst = [5; 2; 8; 1; 9; 3] in
Printf.printf "Original array: ";
Array.iter (fun x -> Printf.printf "%d " x) arr;
print_endline "";
let sorted_arr = sort_array arr in
Printf.printf "Sorted array: ";
Array.iter (fun x -> Printf.printf "%d " x) sorted_arr;
print_endline "";
Printf.printf "Original list: ";
List.iter (fun x -> Printf.printf "%d " x) lst;
print_endline "";
Printf.printf "Sorted list: ";
List.iter (fun x -> Printf.printf "%d " x) (sort_list lst);
print_endline ""Sort descending by using fun a b -> compare b a or reversing after sort.
- Custom:
Array.sort (fun a b -> compare b a) - Reverse:
List.sort compare |> List.rev - By length: Custom comparator
- In-place: Mutating sort
# Sort array descending
let sort_descending arr =
Array.sort (fun a b -> compare b a) arr;
arr
let sort_descending_list lst =
List.sort (fun a b -> compare b a) lst
let sort_descending_generic lst =
List.sort (fun a b ->
match compare a b with
| 0 -> 0
| -1 -> 1
| 1 -> -1
) lst
let sort_by_length_descending lst =
List.sort (fun a b -> compare (String.length b) (String.length a)) lst
let () =
let arr = [|5; 2; 8; 1; 9; 3|] in
let lst = [5; 2; 8; 1; 9; 3] in
let strings = ["apple"; "banana"; "cherry"; "date"] in
Printf.printf "Original array: ";
Array.iter (fun x -> Printf.printf "%d " x) arr;
print_endline "";
let sorted_arr = sort_descending arr in
Printf.printf "Sorted descending: ";
Array.iter (fun x -> Printf.printf "%d " x) sorted_arr;
print_endline "";
Printf.printf "Sorted descending list: ";
List.iter (fun x -> Printf.printf "%d " x) (sort_descending_list lst);
print_endline "";
Printf.printf "By length descending: ";
List.iter (fun x -> Printf.printf "%s " x) (sort_by_length_descending strings);
print_endline ""Flatten nested lists using recursion or List.concat for one level.
- Recursive:
match h with [] -> ... - One level:
List.concat - Deep:
flatten_nested - Performance: O(n) for recursion
# Flatten nested array (list)
let rec flatten = function
| [] -> []
| h :: t ->
(match h with
| [] -> flatten t
| h' :: t' -> h' :: flatten (t' @ t))
let rec flatten_deep = function
| [] -> []
| h :: t ->
if List.length h = 0 then flatten_deep t
else
let h' = List.hd h in
let t' = List.tl h in
h' :: flatten_deep (t' :: t)
let flatten_one_level lst =
List.concat lst
let flatten_nested lst =
let rec helper acc = function
| [] -> List.rev acc
| h :: t ->
if List.length h = 0 then helper acc t
else
let h' = List.hd h in
let t' = List.tl h in
helper (h' :: acc) (t' :: t)
in
helper [] lst
let () =
let nested = [[1; 2]; [3; 4; 5]; [6]; [7; 8; 9; 10]] in
let deeper = [[1; 2]; [3; [4; 5]]] in
Printf.printf "Nested: ";
List.iter (fun sublist ->
Printf.printf "[";
List.iter (fun x -> Printf.printf "%d " x) sublist;
Printf.printf "] "
) nested;
print_endline "";
Printf.printf "Flatten: ";
List.iter (fun x -> Printf.printf "%d " x) (flatten nested);
print_endline "";
Printf.printf "Flatten one level: ";
List.iter (fun x -> Printf.printf "%d " x) (flatten_one_level nested);
print_endline ""Split an array into chunks using List.init and List.drop.
- List:
List.init (min size ...) - Array:
Array.sub - Recursive:
helper acc remaining - Use case: Batch processing
# Chunk array (list)
let chunk_list lst size =
let rec helper acc remaining =
if List.length remaining = 0 then List.rev acc
else
let chunk = List.init (min size (List.length remaining))
(fun i -> List.nth remaining i)
in
let rest = List.drop size remaining in
helper (chunk :: acc) rest
in
helper [] lst
let chunk_array arr size =
let rec helper acc idx =
if idx >= Array.length arr then Array.of_list (List.rev acc)
else
let chunk = Array.sub arr idx (min size (Array.length arr - idx)) in
helper (Array.to_list chunk :: acc) (idx + size)
in
helper [] 0
let chunk_by_predicate lst pred =
let rec helper acc current = function
| [] -> if current = [] then List.rev acc else List.rev (current :: acc)
| h :: t ->
if pred h then
if current = [] then helper acc [h] t
else helper (current :: acc) [h] t
else
helper acc (h :: current) t
in
helper [] [] lst
let () =
let lst = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10] in
let arr = [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|] in
Printf.printf "Chunk list (size 3):\n";
List.iter (fun chunk ->
Printf.printf "[";
List.iter (fun x -> Printf.printf "%d " x) chunk;
Printf.printf "] "
) (chunk_list lst 3);
print_endline "";
Printf.printf "Chunk array (size 3):\n";
let chunks = chunk_array arr 3 in
Array.iter (fun chunk ->
Printf.printf "[";
Array.iter (fun x -> Printf.printf "%d " x) chunk;
Printf.printf "] "
) chunks;
print_endline ""Implement binary search on a sorted array using divide and conquer.
- Recursive:
search low high - Generic:
cmp mid_val target - First occurrence: Search left on match
- Time: O(log n)
# Binary search
let binary_search arr target =
let rec search low high =
if low > high then None
else
let mid = (low + high) / 2 in
let mid_val = arr.(mid) in
if mid_val = target then Some mid
else if mid_val < target then search (mid + 1) high
else search low (mid - 1)
in
search 0 (Array.length arr - 1)
let binary_search_generic arr target cmp =
let rec search low high =
if low > high then None
else
let mid = (low + high) / 2 in
let mid_val = arr.(mid) in
match cmp mid_val target with
| 0 -> Some mid
| -1 -> search (mid + 1) high
| 1 -> search low (mid - 1)
| _ -> None
in
search 0 (Array.length arr - 1)
let binary_search_list lst target =
let arr = Array.of_list lst in
binary_search arr target
let binary_search_first_occurrence arr target =
let rec search low high =
if low > high then None
else
let mid = (low + high) / 2 in
let mid_val = arr.(mid) in
if mid_val = target then
let result = search low (mid - 1) in
match result with
| Some idx -> Some idx
| None -> Some mid
else if mid_val < target then search (mid + 1) high
else search low (mid - 1)
in
search 0 (Array.length arr - 1)
let () =
let arr = [|1; 2; 3; 4; 5; 6; 7|] in
let target = 5 in
match binary_search arr target with
| Some idx -> Printf.printf "Found %d at index %d\n" target idx
| None -> Printf.printf "Not found\n";
let target2 = 8 in
match binary_search arr target2 with
| Some idx -> Printf.printf "Found %d at index %d\n" target2 idx
| None -> Printf.printf "Not found\n"Implement quick sort with pivot selection and partitioning.
- Recursive:
quick_sort = function - In-place:
quick_sort_inplace arr low high - Pivot: Last element
- Time: O(n log n) average
# Quick sort
let rec quick_sort = function
| [] -> []
| pivot :: rest ->
let left = List.filter (fun x -> x <= pivot) rest in
let right = List.filter (fun x -> x > pivot) rest in
quick_sort left @ [pivot] @ quick_sort right
let partition arr low high =
let pivot = arr.(high) in
let i = ref low in
for j = low to high - 1 do
if arr.(j) <= pivot then
begin
let temp = arr.(!i) in
arr.(!i) <- arr.(j);
arr.(j) <- temp;
i := !i + 1
end
done;
let temp = arr.(!i) in
arr.(!i) <- arr.(high);
arr.(high) <- temp;
!i
let rec quick_sort_inplace arr low high =
if low < high then
let pi = partition arr low high in
quick_sort_inplace arr low (pi - 1);
quick_sort_inplace arr (pi + 1) high
let quick_sort_array arr =
let result = Array.copy arr in
quick_sort_inplace result 0 (Array.length result - 1);
result
let quick_sort_optimized = function
| [] -> []
| pivot :: rest ->
let left = List.fold_left (fun acc x -> if x <= pivot then x :: acc else acc) [] rest in
let right = List.fold_left (fun acc x -> if x > pivot then x :: acc else acc) [] rest in
quick_sort_optimized left @ [pivot] @ quick_sort_optimized right
let () =
let lst = [5; 3; 8; 4; 2; 7; 1; 6] in
let arr = [|5; 3; 8; 4; 2; 7; 1; 6|] in
Printf.printf "Original: ";
List.iter (fun x -> Printf.printf "%d " x) lst;
print_endline "";
Printf.printf "Quick sort: ";
List.iter (fun x -> Printf.printf "%d " x) (quick_sort lst);
print_endline "";
Printf.printf "Quick sort (in-place): ";
Array.iter (fun x -> Printf.printf "%d " x) (quick_sort_array arr);
print_endline ""Implement merge sort using divide and conquer with merge.
- Divide:
List.take mid - Merge:
helper acc l1 l2 - In-place: Using temporary array
- Time: O(n log n)
# Merge sort
let rec merge_sort = function
| [] -> []
| [x] -> [x]
| lst ->
let n = List.length lst in
let mid = n / 2 in
let left = List.take mid lst in
let right = List.drop mid lst in
merge (merge_sort left) (merge_sort right)
and merge left right =
let rec helper acc l1 l2 =
match l1, l2 with
| [], [] -> List.rev acc
| [], l2 -> List.rev (List.rev_append acc l2)
| l1, [] -> List.rev (List.rev_append acc l1)
| h1 :: t1, h2 :: t2 ->
if h1 <= h2 then helper (h1 :: acc) t1 l2
else helper (h2 :: acc) l1 t2
in
helper [] left right
let merge_sort_array arr =
let lst = Array.to_list arr in
let sorted = merge_sort lst in
Array.of_list sorted
let merge_sort_inplace arr =
let n = Array.length arr in
let temp = Array.copy arr in
let rec sort low high =
if low < high then
let mid = (low + high) / 2 in
sort low mid;
sort (mid + 1) high;
merge_inplace low mid high
and merge_inplace low mid high =
for i = low to high do
temp.(i) <- arr.(i)
done;
let i = ref low in
let j = ref (mid + 1) in
for k = low to high do
if !i > mid then
begin
arr.(k) <- temp.(!j);
j := !j + 1
end
else if !j > high then
begin
arr.(k) <- temp.(!i);
i := !i + 1
end
else if temp.(!i) <= temp.(!j) then
begin
arr.(k) <- temp.(!i);
i := !i + 1
end
else
begin
arr.(k) <- temp.(!j);
j := !j + 1
end
done
in
sort 0 (n - 1)
let () =
let lst = [5; 3; 8; 4; 2; 7; 1; 6] in
let arr = [|5; 3; 8; 4; 2; 7; 1; 6|] in
Printf.printf "Original: ";
List.iter (fun x -> Printf.printf "%d " x) lst;
print_endline "";
Printf.printf "Merge sort: ";
List.iter (fun x -> Printf.printf "%d " x) (merge_sort lst);
print_endline "";
let sorted_arr = merge_sort_array arr in
Printf.printf "Merge sort array: ";
Array.iter (fun x -> Printf.printf "%d " x) sorted_arr;
print_endline "";
merge_sort_inplace arr;
Printf.printf "Merge sort in-place: ";
Array.iter (fun x -> Printf.printf "%d " x) arr;
print_endline ""Implement bubble sort with optimization to stop early if no swaps occur.
- Basic:
for i = 0 to n-2 do for j = 0 to n-i-2 - Optimized:
swapped ref - Time: O(n²) worst case
- Use case: Small datasets
# Bubble sort
let bubble_sort arr =
let n = Array.length arr in
let sorted_arr = Array.copy arr in
for i = 0 to n - 2 do
for j = 0 to n - i - 2 do
if sorted_arr.(j) > sorted_arr.(j + 1) then
let temp = sorted_arr.(j) in
sorted_arr.(j) <- sorted_arr.(j + 1);
sorted_arr.(j + 1) <- temp
done
done;
sorted_arr
let bubble_sort_optimized arr =
let n = Array.length arr in
let sorted_arr = Array.copy arr in
for i = 0 to n - 2 do
let swapped = ref false in
for j = 0 to n - i - 2 do
if sorted_arr.(j) > sorted_arr.(j + 1) then
begin
let temp = sorted_arr.(j) in
sorted_arr.(j) <- sorted_arr.(j + 1);
sorted_arr.(j + 1) <- temp;
swapped := true
end
done;
if not !swapped then ()
done;
sorted_arr
let bubble_sort_list lst =
let arr = Array.of_list lst in
let sorted = bubble_sort arr in
Array.to_list sorted
let () =
let arr = [|5; 3; 8; 4; 2; 7; 1; 6|] in
let lst = [5; 3; 8; 4; 2; 7; 1; 6] in
Printf.printf "Original: ";
Array.iter (fun x -> Printf.printf "%d " x) arr;
print_endline "";
Printf.printf "Bubble sort: ";
Array.iter (fun x -> Printf.printf "%d " x) (bubble_sort arr);
print_endline "";
Printf.printf "Bubble sort optimized: ";
Array.iter (fun x -> Printf.printf "%d " x) (bubble_sort_optimized arr);
print_endline "";
Printf.printf "Bubble sort list: ";
List.iter (fun x -> Printf.printf "%d " x) (bubble_sort_list lst);
print_endline ""Find intersection using filter and membership test, or sets.
- Fold:
List.fold_left compose - Operator:
>>,<< - Recursion:
compose_list - Direction: Right to left
# Intersection of arrays
let intersection lst1 lst2 =
let set2 = List.fold_left (fun acc x -> StringSet.add x acc) StringSet.empty lst2 in
List.filter (fun x -> StringSet.mem x set2) lst1
let intersection_int lst1 lst2 =
let set2 = List.fold_left (fun acc x -> IntSet.add x acc) IntSet.empty lst2 in
List.filter (fun x -> IntSet.mem x set2) lst1
let intersection_simple lst1 lst2 =
List.filter (fun x -> List.mem x lst2) lst1
let intersection_unique lst1 lst2 =
let result = intersection_simple lst1 lst2 in
remove_duplicates result
let intersection_multiple lists =
match lists with
| [] -> []
| h :: t -> List.fold_left intersection_simple h t
let () =
let lst1 = ["apple"; "banana"; "orange"; "grape"; "kiwi"] in
let lst2 = ["banana"; "kiwi"; "mango"; "grape"] in
let ints1 = [1; 2; 3; 4; 5] in
let ints2 = [4; 5; 6; 7; 8] in
Printf.printf "Intersection: ";
List.iter (fun x -> Printf.printf "%s " x) (intersection lst1 lst2);
print_endline "";
Printf.printf "Intersection (simple): ";
List.iter (fun x -> Printf.printf "%s " x) (intersection_simple lst1 lst2);
print_endline "";
Printf.printf "Intersection (ints): ";
List.iter (fun x -> Printf.printf "%d " x) (intersection_int ints1 ints2);
print_endline ""Union arrays using sets or concatenation with duplicate removal.
- Set:
StringSet.to_list - Simple:
lst1 @ List.filter ... - Many:
List.concat |> set |> to_list - Time: O(n+m)
# Union of arrays
let union lst1 lst2 =
let set = List.fold_left (fun acc x -> StringSet.add x acc) StringSet.empty (lst1 @ lst2) in
StringSet.to_list set
let union_int lst1 lst2 =
let set = List.fold_left (fun acc x -> IntSet.add x acc) IntSet.empty (lst1 @ lst2) in
IntSet.to_list set
let union_simple lst1 lst2 =
lst1 @ List.filter (fun x -> not (List.mem x lst1)) lst2
let union_unique lst1 lst2 =
let combined = lst1 @ lst2 in
remove_duplicates combined
let union_many lists =
let all = List.concat lists in
let set = List.fold_left (fun acc x -> StringSet.add x acc) StringSet.empty all in
StringSet.to_list set
let () =
let lst1 = ["apple"; "banana"; "orange"] in
let lst2 = ["orange"; "grape"; "kiwi"] in
let ints1 = [1; 2; 3; 4] in
let ints2 = [4; 5; 6; 7] in
Printf.printf "Union: ";
List.iter (fun x -> Printf.printf "%s " x) (union lst1 lst2);
print_endline "";
Printf.printf "Union (simple): ";
List.iter (fun x -> Printf.printf "%s " x) (union_simple lst1 lst2);
print_endline "";
Printf.printf "Union (ints): ";
List.iter (fun x -> Printf.printf "%d " x) (union_int ints1 ints2);
print_endline ""Find difference using filter and membership test, or sets.
- Filter:
List.filter (fun x -> not (List.mem x lst2)) - Symmetric:
diff1 @ diff2 - Set:
StringSet.mem - All: Fold over lists
# Difference of arrays
let difference lst1 lst2 =
List.filter (fun x -> not (List.mem x lst2)) lst1
let difference_set lst1 lst2 =
let set2 = List.fold_left (fun acc x -> StringSet.add x acc) StringSet.empty lst2 in
List.filter (fun x -> not (StringSet.mem x set2)) lst1
let symmetric_difference lst1 lst2 =
(difference lst1 lst2) @ (difference lst2 lst1)
let difference_all lists =
match lists with
| [] -> []
| h :: t -> List.fold_left (fun acc lst -> difference acc lst) h t
let () =
let lst1 = ["apple"; "banana"; "orange"; "grape"] in
let lst2 = ["banana"; "kiwi"; "grape"] in
let ints1 = [1; 2; 3; 4; 5] in
let ints2 = [4; 5; 6; 7; 8] in
Printf.printf "Difference: ";
List.iter (fun x -> Printf.printf "%s " x) (difference lst1 lst2);
print_endline "";
Printf.printf "Symmetric difference: ";
List.iter (fun x -> Printf.printf "%s " x) (symmetric_difference lst1 lst2);
print_endline "";
Printf.printf "Difference (ints): ";
List.iter (fun x -> Printf.printf "%d " x) (difference ints1 ints2);
print_endline ""Group records by a property using a hashtable for efficient grouping.
- Hashtbl:
Hashtbl.findandHashtbl.replace - Key function:
fun p -> p.age - Aggregation:
group_and_sum - Time: O(n)
# Group by property
type person = {
name: string;
age: int;
city: string;
}
let group_by_property lst key =
let groups = Hashtbl.create 10 in
List.iter (fun item ->
let key_value = key item in
if Hashtbl.mem groups key_value then
let current = Hashtbl.find groups key_value in
Hashtbl.replace groups key_value (item :: current)
else
Hashtbl.add groups key_value [item]
) lst;
Hashtbl.fold (fun k v acc -> (k, List.rev v) :: acc) groups []
let group_by_age persons =
group_by_property persons (fun p -> p.age)
let group_by_city persons =
group_by_property persons (fun p -> p.city)
let group_and_sum lst key value =
let groups = Hashtbl.create 10 in
List.iter (fun item ->
let key_value = key item in
let value_to_add = value item in
if Hashtbl.mem groups key_value then
let current = Hashtbl.find groups key_value in
Hashtbl.replace groups key_value (current + value_to_add)
else
Hashtbl.add groups key_value value_to_add
) lst;
Hashtbl.fold (fun k v acc -> (k, v) :: acc) groups []
let () =
let people = [
{ name = "Alice"; age = 25; city = "NYC" };
{ name = "Bob"; age = 30; city = "LA" };
{ name = "Charlie"; age = 25; city = "NYC" };
{ name = "David"; age = 35; city = "Chicago" };
{ name = "Eve"; age = 30; city = "LA" };
] in
Printf.printf "Group by age:\n";
List.iter (fun (age, persons) ->
Printf.printf "Age %d: " age;
List.iter (fun p -> Printf.printf "%s " p.name) persons;
print_endline ""
) (group_by_age people);
Printf.printf "Group by city:\n";
List.iter (fun (city, persons) ->
Printf.printf "City %s: " city;
List.iter (fun p -> Printf.printf "%s " p.name) persons;
print_endline ""
) (group_by_city people)Deep clone objects using List.map or Array.copy for shallow copy.
- List:
List.map (fun x -> x) - Array:
Array.copy - Hashtbl:
Hashtbl.createandHashtbl.add - Record: Copy by value
# Deep clone object
let rec deep_clone obj =
match obj with
| [] -> []
| h :: t ->
(match h with
| [] -> [] :: deep_clone t
| h' :: t' -> (h' :: t') :: deep_clone t)
let deep_clone_record obj =
obj
let deep_clone_array arr =
Array.copy arr
let deep_clone_hashtbl tbl =
let new_tbl = Hashtbl.create (Hashtbl.length tbl) in
Hashtbl.iter (fun k v -> Hashtbl.add new_tbl k v) tbl;
new_tbl
let deep_clone_tuple (a, b, c) =
(a, b, c)
let rec deep_clone_generic obj =
match obj with
| [] -> []
| h :: t ->
(match h with
| [] -> [] :: deep_clone_generic t
| h' :: t' -> (h' :: t') :: deep_clone_generic t)
| _ -> obj
let () =
let original = [1; 2; 3] in
let cloned = List.map (fun x -> x) original in
Printf.printf "Original: ";
List.iter (fun x -> Printf.printf "%d " x) original;
print_endline "";
Printf.printf "Cloned: ";
List.iter (fun x -> Printf.printf "%d " x) cloned;
print_endline ""Perform immutable updates by copying and modifying nested structures.
- List:
update_immutable - Path:
key :: rest - Recursive: Helper function
- Use case: State management
# Immutable update
let update_immutable obj path value =
match path with
| [] -> value
| [key] ->
(match obj with
| [] -> [(key, value)]
| h :: t ->
if fst h = key then (key, value) :: t
else h :: update_immutable t [key] value)
| key :: rest ->
(match obj with
| [] -> [(key, update_immutable [] rest value)]
| h :: t ->
if fst h = key then (key, update_immutable (snd h) rest value) :: t
else h :: update_immutable t (key :: rest) value)
let update_nested obj path value =
let rec helper obj path =
match path, obj with
| [], _ -> value
| [key], [(k, v)] when k = key -> [(key, value)]
| [key], (k, v) :: t when k = key -> (key, value) :: t
| [key], [] -> [(key, value)]
| key :: rest, (k, v) :: t when k = key ->
(k, helper v rest) :: t
| key :: rest, [] -> [(key, helper [] rest)]
| key :: rest, h :: t -> h :: helper t (key :: rest)
in
helper obj path
let set_field obj field value =
List.map (fun (key, val) ->
if key = field then (key, value)
else (key, val)
) obj
let () =
let state = [("user", [("name", "Alice"); ("age", "25")])] in
Printf.printf "Original state: ";
List.iter (fun (k, v) ->
Printf.printf "%s: " k;
match v with
| [] -> print_endline "[]"
| _ -> List.iter (fun (k2, v2) -> Printf.printf "%s=%s " k2 v2) v
) state;
print_endline "";
let new_state = update_immutable state ["user"; "age"] "26" in
Printf.printf "Updated state: ";
List.iter (fun (k, v) ->
Printf.printf "%s: " k;
match v with
| [] -> print_endline "[]"
| _ -> List.iter (fun (k2, v2) -> Printf.printf "%s=%s " k2 v2) v
) new_state;
print_endline ""Implement pipe using fold or composition for function chaining.
- Fold:
List.fold_left (fun acc f -> f acc) - Operator:
|> - Composition:
compose - Direction: Left to right
# Pipe function
let pipe fns value =
List.fold_left (fun acc f -> f acc) value fns
let pipe_operator f g x = g (f x)
let (|>) x f = f x
let (<|) f x = f x
let compose f g x = f (g x)
let pipe_with_logging fns value =
let rec helper value = function
| [] -> value
| f :: rest ->
let result = f value in
Printf.printf "Value: %s\n" (string_of_int result);
helper result rest
in
helper value fns
let () =
let double x = x * 2 in
let add_ten x = x + 10 in
let square x = x * x in
let result = pipe [double; add_ten; square] 5 in
Printf.printf "Pipe: %d\n" result;
let result2 = 5 |> double |> add_ten |> square in
Printf.printf "Pipe operator: %d\n" result2;
let result3 = square (add_ten (double 5)) in
Printf.printf "Direct: %d\n" result3;
let composed = compose square (compose add_ten double) in
Printf.printf "Composed: %d\n" (composed 5)Implement compose using recursion or fold for function composition.
- Fold:
List.fold_left compose - Operator:
>>,<< - Recursion:
compose_list - Direction: Right to left
# Compose function
let compose f g x = f (g x)
let compose_list fns =
List.fold_left compose (fun x -> x) fns
let compose_right f g x = g (f x)
let compose_all fns x =
List.fold_right (fun f acc -> f acc) fns x
let pipe_operator f g x = g (f x)
let (>>) f g x = g (f x)
let (<<) f g x = f (g x)
let compose_with_logging fns =
let rec helper acc = function
| [] -> acc
| f :: rest ->
let composed = fun x ->
let result = f (acc x) in
Printf.printf "Result: %d\n" result;
result
in
helper composed rest
in
helper (fun x -> x) (List.rev fns)
let () =
let double x = x * 2 in
let add_ten x = x + 10 in
let square x = x * x in
let composed = compose square (compose add_ten double) in
Printf.printf "Composed: %d\n" (composed 5);
let composed2 = compose_list [double; add_ten; square] in
Printf.printf "Composed list: %d\n" (composed2 5);
let result = 5 |> double |> add_ten |> square in
Printf.printf "Pipe: %d\n" result;
let result2 = (double >> add_ten >> square) 5 in
Printf.printf "Compose operator: %d\n" result2Implement memoization using a hashtable to cache function results.
- Cache:
Hashtbl.create - Check:
Hashtbl.mem - Limit:
memoize_with_limit - Multiple args: Tuple as key
# Memoization
let memoize f =
let cache = Hashtbl.create 100 in
fun x ->
if Hashtbl.mem cache x then
Hashtbl.find cache x
else
let result = f x in
Hashtbl.add cache x result;
result
let memoize_fib =
let rec fib n =
if n <= 1 then n
else fib (n - 1) + fib (n - 2)
in
memoize fib
let memoize_with_limit f limit =
let cache = Hashtbl.create 100 in
let count = ref 0 in
fun x ->
if !count >= limit then
(Hashtbl.clear cache; count := 0);
if Hashtbl.mem cache x then
Hashtbl.find cache x
else
let result = f x in
Hashtbl.add cache x result;
count := !count + 1;
result
let memoize_multiple f =
let cache = Hashtbl.create 100 in
fun x y ->
let key = (x, y) in
if Hashtbl.mem cache key then
Hashtbl.find cache key
else
let result = f x y in
Hashtbl.add cache key result;
result
let () =
let start = Unix.time () in
let result1 = memoize_fib 35 in
let time1 = Unix.time () -. start in
let start2 = Unix.time () in
let result2 = memoize_fib 35 in
let time2 = Unix.time () -. start2 in
Printf.printf "First: %d (%.6fs)\n" result1 time1;
Printf.printf "Second: %d (%.6fs)\n" result2 time2;
let add_memo = memoize_multiple (fun x y -> x + y) in
Printf.printf "Memoized add: %d\n" (add_memo 5 3);
Printf.printf "Memoized add (cached): %d\n" (add_memo 5 3)Implement once function that ensures a function is called only once.
- Flag:
called ref - Result:
result ref option - Reset:
once_with_reset - Async:
once_async
# Once function
let once f =
let called = ref false in
let result = ref None in
fun x ->
if not !called then
begin
called := true;
result := Some (f x)
end;
match !result with
| Some v -> v
| None -> failwith "Unexpected None"
let once_multiple f =
let called = ref false in
let result = ref None in
fun args ->
if not !called then
begin
called := true;
result := Some (f args)
end;
match !result with
| Some v -> v
| None -> failwith "Unexpected None"
let once_with_reset f =
let called = ref false in
let result = ref None in
let reset () =
called := false;
result := None
in
let fn x =
if not !called then
begin
called := true;
result := Some (f x)
end;
match !result with
| Some v -> v
| None -> failwith "Unexpected None"
in
(fn, reset)
let once_async f =
let called = ref false in
let result = ref None in
let promise = ref None in
fun x ->
if not !called then
begin
called := true;
let p, r = Lwt.task () in
promise := Some (p, r);
let _ = Lwt.async (fun () ->
let value = f x in
result := Some value;
Lwt.wakeup_exn (snd (Option.get !promise)) value;
Lwt.return ()
) in
fst (Option.get !promise)
end
else
match !result with
| Some v -> Lwt.return v
| None -> fst (Option.get !promise)
let () =
let initialize = once (fun x -> Printf.printf "Initialized with %d\n" x; x * 2) in
Printf.printf "First: %d\n" (initialize 10);
Printf.printf "Second: %d\n" (initialize 20);
let (fn, reset) = once_with_reset (fun x -> x * 2) in
Printf.printf "First with reset: %d\n" (fn 10);
reset ();
Printf.printf "After reset: %d\n" (fn 20)Implement debounce with leading edge using timer and last call time.
- Timer:
Thread.delay - State:
last_call ref - Timeout:
timeout ref option - Callback:
debounce_leading_with_callback
# Debounce with leading edge
let debounce_leading f delay =
let last_call = ref 0.0 in
let timeout = ref None in
fun x ->
let now = Unix.time () in
if now -. !last_call >= delay then
begin
last_call := now;
f x
end
else
match !timeout with
| Some _ -> ()
| None ->
let _ = Thread.create (fun () ->
Thread.delay delay;
timeout := None;
last_call := Unix.time ();
f x
) () in
()
let debounce_leading_with_callback f delay callback =
let last_call = ref 0.0 in
let timeout = ref None in
fun x ->
let now = Unix.time () in
if now -. !last_call >= delay then
begin
last_call := now;
let result = f x in
callback result
end
else
match !timeout with
| Some _ -> ()
| None ->
let _ = Thread.create (fun () ->
Thread.delay delay;
timeout := None;
last_call := Unix.time ();
let result = f x in
callback result
) () in
()
let debounce_leading_async f delay =
let last_call = ref 0.0 in
let timeout = ref None in
fun x ->
let now = Unix.time () in
if now -. !last_call >= delay then
begin
last_call := now;
Lwt.return (f x)
end
else
match !timeout with
| Some _ -> Lwt.return ()
| None ->
let p, r = Lwt.task () in
timeout := Some (p, r);
let _ = Lwt.async (fun () ->
Lwt_unix.sleep delay >>= fun () ->
timeout := None;
last_call := Unix.time ();
let result = f x in
Lwt.wakeup r result;
Lwt.return ()
) in
p
let () =
Printf.printf "Debounce examples available\n"Implement throttle with leading edge by checking time since last call.
- State:
last_call ref - Skipped: Track skipped calls
- Trailing:
throttle_with_trailing - Use case: Rate limiting
# Throttle with leading edge
let throttle_leading f delay =
let last_call = ref 0.0 in
fun x ->
let now = Unix.time () in
if now -. !last_call >= delay then
begin
last_call := now;
f x
end
let throttle_leading_with_skipped f delay =
let last_call = ref 0.0 in
let skipped = ref 0 in
fun x ->
let now = Unix.time () in
if now -. !last_call >= delay then
begin
if !skipped > 0 then
begin
Printf.printf "Skipped %d calls\n" !skipped;
skipped := 0
end;
last_call := now;
f x
end
else
skipped := !skipped + 1
let throttle_leading_async f delay =
let last_call = ref 0.0 in
fun x ->
let now = Unix.time () in
if now -. !last_call >= delay then
begin
last_call := now;
Lwt.return (f x)
end
else
Lwt.return ()
let throttle_with_trailing f delay =
let last_call = ref 0.0 in
let pending = ref None in
let timer = ref None in
fun x ->
let now = Unix.time () in
if now -. !last_call >= delay then
begin
last_call := now;
f x
end
else
begin
pending := Some x;
match !timer with
| Some _ -> ()
| None ->
let _ = Thread.create (fun () ->
Thread.delay delay;
timer := None;
last_call := Unix.time ();
match !pending with
| Some value -> f value
| None -> ()
) () in
()
end
let () =
Printf.printf "Throttle examples available\n"Implement deep equality comparison for nested structures.
- Recursive:
deep_equal - Lists: Compare elements
- Hashtbl: Compare keys and values
- Records: Compare fields
# Deep equal
let rec deep_equal obj1 obj2 =
match obj1, obj2 with
| [], [] -> true
| [], _ -> false
| _, [] -> false
| h1 :: t1, h2 :: t2 ->
(match h1, h2 with
| [], [] -> true
| [], _ -> false
| _, [] -> false
| h1' :: t1', h2' :: t2' ->
h1' = h2' && deep_equal t1' t2')
&& deep_equal t1 t2
| _ -> obj1 = obj2
let rec deep_equal_record obj1 obj2 =
obj1 = obj2
let rec deep_equal_hashtbl tbl1 tbl2 =
if Hashtbl.length tbl1 <> Hashtbl.length tbl2 then false
else
let rec check keys =
match keys with
| [] -> true
| k :: rest ->
if Hashtbl.mem tbl1 k && Hashtbl.mem tbl2 k then
let v1 = Hashtbl.find tbl1 k in
let v2 = Hashtbl.find tbl2 k in
if v1 = v2 then check rest
else false
else false
in
check (Hashtbl.fold (fun k _ acc -> k :: acc) tbl1 [])
let deep_equal_generic obj1 obj2 =
match obj1, obj2 with
| [] , [] -> true
| [] , _ -> false
| _ , [] -> false
| h1 :: t1, h2 :: t2 ->
h1 = h2 && deep_equal_generic t1 t2
| _ -> obj1 = obj2
let () =
let lst1 = [1; 2; 3] in
let lst2 = [1; 2; 3] in
let lst3 = [1; 2; 4] in
Printf.printf "lst1 = lst2: %b\n" (deep_equal lst1 lst2);
Printf.printf "lst1 = lst3: %b\n" (deep_equal lst1 lst3);
let nested1 = [[1; 2]; [3; 4]] in
let nested2 = [[1; 2]; [3; 4]] in
let nested3 = [[1; 2]; [3; 5]] in
Printf.printf "nested1 = nested2: %b\n" (deep_equal nested1 nested2);
Printf.printf "nested1 = nested3: %b\n" (deep_equal nested1 nested3)Implement observable pattern with subscribers and notification.
- Observable:
create_observable - Subscribe:
subscribe obs callback - Notify:
notify obs data - Transform:
create_observable_transform
# Observable pattern
type 'a observer = 'a -> unit
type 'a observable = {
mutable subscribers: 'a observer list;
}
let create_observable () =
{ subscribers = [] }
let subscribe obs callback =
obs.subscribers <- callback :: obs.subscribers
let unsubscribe obs callback =
obs.subscribers <- List.filter (fun f -> f != callback) obs.subscribers
let notify obs data =
List.iter (fun callback -> callback data) obs.subscribers
let create_observable_with_state initial_state =
let obs = create_observable () in
let state = ref initial_state in
let set_state new_state =
state := new_state;
notify obs new_state
in
let get_state () = !state in
(obs, set_state, get_state)
let create_observable_transform transform =
let obs = create_observable () in
fun data ->
let transformed = transform data in
notify obs transformed
let () =
let obs = create_observable () in
let callback1 data = Printf.printf "Observer1: %s\n" data in
let callback2 data = Printf.printf "Observer2: %s\n" data in
subscribe obs callback1;
subscribe obs callback2;
Printf.printf "Notifying observers:\n";
notify obs "Hello, World!";
unsubscribe obs callback1;
Printf.printf "After unsubscribing observer1:\n";
notify obs "Hello again!";
let (obs2, set_state, get_state) = create_observable_with_state 0 in
subscribe obs2 (fun state -> Printf.printf "State changed to: %d\n" state);
Printf.printf "Current state: %d\n" (get_state ());
set_state 10;
set_state 20Implement singleton pattern using module-level state or closures.
- Module:
Singleton.get_instance - Closure:
create_singleton init_func - Mutable:
singleton_mutable - Lazy: Create on first use
# Singleton pattern
module Singleton = struct
let instance = ref None
let get_instance init_func =
match !instance with
| Some inst -> inst
| None ->
let inst = init_func () in
instance := Some inst;
inst
end
let create_singleton init_func =
let instance = ref None in
fun () ->
match !instance with
| Some inst -> inst
| None ->
let inst = init_func () in
instance := Some inst;
inst
let singleton_with_data init_func =
let instance = ref None in
let data = ref None in
fun () ->
match !instance with
| Some inst -> (inst, !data)
| None ->
let inst = init_func () in
instance := Some inst;
data := Some (Hashtbl.create 10);
(inst, !data)
let singleton_mutable init_func =
let instance = ref None in
let mutable_data = ref None in
fun () ->
match !instance with
| Some inst -> inst
| None ->
let inst = init_func () in
instance := Some inst;
mutable_data := Some (Hashtbl.create 10);
inst
let () =
let get_config = create_singleton (fun () ->
Printf.printf "Initializing singleton\n";
{ name = "App"; version = 1.0 }
) in
let config1 = get_config () in
let config2 = get_config () in
Printf.printf "config1 = config2: %b\n" (config1 == config2);
Printf.printf "config1.name = %s\n" config1.name;
Printf.printf "config2.name = %s\n" config2.nameImplement factory pattern for creating objects without specifying concrete classes.
- Function:
create_user user_type name - Module:
UserFactory - Pattern: Return variant types
- Custom:
create_user_with_permissions
# Factory pattern
type user =
| Admin of string
| Guest of string
| RegularUser of string
let create_user user_type name =
match user_type with
| "admin" -> Admin name
| "guest" -> Guest name
| _ -> RegularUser name
let describe_user = function
| Admin name -> Printf.sprintf "Admin: %s" name
| Guest name -> Printf.sprintf "Guest: %s" name
| RegularUser name -> Printf.sprintf "Regular User: %s" name
let create_user_with_id user_type name id =
match user_type with
| "admin" -> (Admin name, id)
| "guest" -> (Guest name, id)
| _ -> (RegularUser name, id)
module UserFactory = struct
let create_admin name = Admin name
let create_guest name = Guest name
let create_regular name = RegularUser name
end
let create_user_with_permissions user_type name permissions =
match user_type with
| "admin" -> (Admin name, permissions)
| "guest" -> (Guest name, [])
| _ -> (RegularUser name, [])
let () =
let user1 = create_user "admin" "Alice" in
let user2 = create_user "guest" "Bob" in
let user3 = create_user "regular" "Charlie" in
Printf.printf "%s\n" (describe_user user1);
Printf.printf "%s\n" (describe_user user2);
Printf.printf "%s\n" (describe_user user3);
let admin = UserFactory.create_admin "Alice" in
let guest = UserFactory.create_guest "Bob" in
Printf.printf "Factory: %s\n" (describe_user admin);
Printf.printf "Factory: %s\n" (describe_user guest)Implement strategy pattern with interchangeable payment methods.
- Strategy:
payment_strategy - Context:
process_payment strategy amount - Functions:
pay_with_credit_card - Decorators:
apply_discount
# Strategy pattern
type payment_strategy =
| CreditCard
| PayPal
| Crypto
let pay_with_credit_card amount =
Printf.printf "Paid %f with Credit Card\n" amount
let pay_with_paypal amount =
Printf.printf "Paid %f with PayPal\n" amount
let pay_with_crypto amount =
Printf.printf "Paid %f with Crypto\n" amount
let payment_strategy = function
| CreditCard -> pay_with_credit_card
| PayPal -> pay_with_paypal
| Crypto -> pay_with_crypto
let process_payment strategy amount =
let payment_func = payment_strategy strategy in
payment_func amount
let apply_discount discount strategy amount =
let discounted_amount = amount *. (1.0 -. discount) in
let payment_func = payment_strategy strategy in
payment_func discounted_amount
let with_logging strategy amount =
Printf.printf "Processing payment of %f\n" amount;
let payment_func = payment_strategy strategy in
payment_func amount;
Printf.printf "Payment completed\n"
let () =
Printf.printf "Strategy pattern examples:\n";
process_payment CreditCard 100.0;
process_payment PayPal 50.0;
process_payment Crypto 75.0;
apply_discount 0.1 CreditCard 100.0;
with_logging PayPal 60.0Implement observer pattern with subject state management.
- Subject:
create_subject initial_state - Attach:
attach subject observer - Notify:
set_state subject new_state - Derived:
create_derived_subject
# Observer pattern
type 'a observer = 'a -> unit
type 'a subject = {
mutable observers: 'a observer list;
mutable state: 'a;
}
let create_subject initial_state =
{ observers = []; state = initial_state }
let attach subject observer =
subject.observers <- observer :: subject.observers
let detach subject observer =
subject.observers <- List.filter (fun f -> f != observer) subject.observers
let notify subject =
List.iter (fun observer -> observer subject.state) subject.observers
let set_state subject new_state =
subject.state <- new_state;
notify subject
let create_derived_subject subject transform =
let obs = create_subject (transform subject.state) in
attach subject (fun state ->
set_state obs (transform state)
);
obs
let create_filtered_subject subject predicate =
let obs = create_subject subject.state in
attach subject (fun state ->
if predicate state then set_state obs state
);
obs
let () =
let subject = create_subject "Initial state" in
let observer1 state = Printf.printf "Observer1: %s\n" state in
let observer2 state = Printf.printf "Observer2: %s\n" state in
attach subject observer1;
attach subject observer2;
Printf.printf "Setting state:\n";
set_state subject "Hello, World!";
set_state subject "Another update";
detach subject observer1;
Printf.printf "After detaching observer1:\n";
set_state subject "Final state"Implement decorator pattern for adding features to coffee.
- Component:
create_coffee cost description - Decorators:
milk_decorator,sugar_decorator - Application:
apply_decorators coffee decorators - Chaining:
milk_decorator (sugar_decorator coffee)
# Decorator pattern
type coffee = {
cost: float;
description: string;
}
let create_coffee cost description =
{ cost; description }
let milk_decorator coffee =
{ cost = coffee.cost +. 2.0; description = coffee.description ^ ", Milk" }
let sugar_decorator coffee =
{ cost = coffee.cost +. 1.0; description = coffee.description ^ ", Sugar" }
let caramel_decorator coffee =
{ cost = coffee.cost +. 2.5; description = coffee.description ^ ", Caramel" }
let whipped_cream_decorator coffee =
{ cost = coffee.cost +. 1.5; description = coffee.description ^ ", Whipped Cream" }
let apply_decorators coffee decorators =
List.fold_left (fun acc dec -> dec acc) coffee decorators
let () =
let coffee = create_coffee 5.0 "Coffee" in
Printf.printf "Base: %s ($%.2f)\n" coffee.description coffee.cost;
let with_milk = milk_decorator coffee in
Printf.printf "With milk: %s ($%.2f)\n" with_milk.description with_milk.cost;
let with_sugar = sugar_decorator coffee in
Printf.printf "With sugar: %s ($%.2f)\n" with_sugar.description with_sugar.cost;
let with_milk_sugar = sugar_decorator (milk_decorator coffee) in
Printf.printf "With milk and sugar: %s ($%.2f)\n" with_milk_sugar.description with_milk_sugar.cost;
let decorators = [milk_decorator; sugar_decorator; caramel_decorator] in
let fully_decorated = apply_decorators coffee decorators in
Printf.printf "Fully decorated: %s ($%.2f)\n" fully_decorated.description fully_decorated.costImplement command pattern with execute, undo, and redo operations.
- Command:
create_add_command receiver value - Macro:
create_macro_command commands - History:
create_command_history - Operations:
execute_command,undo_command
# Command pattern
type 'a command = {
execute: unit -> unit;
undo: unit -> unit;
redo: unit -> unit;
}
let create_add_command receiver value =
let execute () = receiver := !receiver + value in
let undo () = receiver := !receiver - value in
let redo () = execute () in
{ execute; undo; redo }
let create_subtract_command receiver value =
let execute () = receiver := !receiver - value in
let undo () = receiver := !receiver + value in
let redo () = execute () in
{ execute; undo; redo }
let create_macro_command commands =
let execute () = List.iter (fun cmd -> cmd.execute ()) commands in
let undo () = List.iter (fun cmd -> cmd.undo ()) (List.rev commands) in
let redo () = execute () in
{ execute; undo; redo }
let execute_command command = command.execute ()
let undo_command command = command.undo ()
let redo_command command = command.redo ()
let create_command_history () =
let history = ref [] in
let current = ref 0 in
let execute cmd =
execute_command cmd;
history := List.take !current !history @ [cmd] @ List.drop !current !history;
current := !current + 1
in
let undo () =
if !current > 0 then
let cmd = List.nth !history (!current - 1) in
cmd.undo ();
current := !current - 1
in
let redo () =
if !current < List.length !history then
let cmd = List.nth !history !current in
cmd.redo ();
current := !current + 1
in
(execute, undo, redo)
let () =
let counter = ref 0 in
let add_cmd = create_add_command counter 5 in
let sub_cmd = create_subtract_command counter 3 in
Printf.printf "Initial: %d\n" !counter;
execute_command add_cmd;
Printf.printf "After add: %d\n" !counter;
execute_command sub_cmd;
Printf.printf "After sub: %d\n" !counter;
undo_command sub_cmd;
Printf.printf "After undo: %d\n" !counter;
redo_command sub_cmd;
Printf.printf "After redo: %d\n" !counter;
let macro = create_macro_command [add_cmd; add_cmd; sub_cmd] in
execute_command macro;
Printf.printf "After macro: %d\n" !counterImplement memento pattern for state capture and restoration.
- Originator:
create_originator - Memento:
save_state - Caretaker:
create_caretaker - Undo/Redo:
save_to_caretaker,undo,redo
# Memento pattern
type 'a memento = {
state: 'a;
}
type 'a originator = {
mutable state: 'a;
}
let create_originator initial_state =
{ state = initial_state }
let save_state originator =
{ state = originator.state }
let restore_state originator memento =
originator.state <- memento.state
let update_state originator new_state =
originator.state <- new_state
type 'a caretaker = {
mementos: 'a memento list;
current: int;
}
let create_caretaker () =
{ mementos = []; current = 0 }
let save_to_caretaker caretaker memento =
let new_mementos = List.take caretaker.current caretaker.mementos @ [memento] in
{ caretaker with mementos = new_mementos; current = caretaker.current + 1 }
let undo caretaker =
if caretaker.current > 0 then
let new_current = caretaker.current - 1 in
let memento = List.nth caretaker.mementos new_current in
({ caretaker with current = new_current }, Some memento)
else
(caretaker, None)
let redo caretaker =
if caretaker.current < List.length caretaker.mementos then
let memento = List.nth caretaker.mementos caretaker.current in
let new_current = caretaker.current + 1 in
({ caretaker with current = new_current }, Some memento)
else
(caretaker, None)
let () =
let originator = create_originator { value = 0 } in
let caretaker = create_caretaker () in
let save_and_restore new_state =
update_state originator new_state;
let memento = save_state originator in
let caretaker2 = save_to_caretaker caretaker memento in
caretaker2
in
let save_state_value value =
update_state originator { value };
save_state originator
in
let state1 = save_state_value 1 in
let state2 = save_state_value 2 in
let state3 = save_state_value 3 in
Printf.printf "Current: %d\n" originator.state.value;
let (caretaker2, memento) = undo caretaker in
match memento with
| Some m ->
restore_state originator m;
Printf.printf "After undo: %d\n" originator.state.value
| None -> print_endline "Cannot undo";
let (caretaker3, memento2) = redo caretaker2 in
match memento2 with
| Some m ->
restore_state originator m;
Printf.printf "After redo: %d\n" originator.state.value
| None -> print_endline "Cannot redo"Implement mediator pattern for decoupled communication between colleagues.
- Mediator:
create_mediator - Colleague:
create_colleague name mediator - Send:
send mediator message - State:
create_colleague_with_state
# Mediator pattern
type 'a mediator = {
mutable colleagues: ('a -> unit) list;
}
let create_mediator () =
{ colleagues = [] }
let register mediator colleague =
mediator.colleagues <- colleague :: mediator.colleagues
let send mediator message =
List.iter (fun colleague -> colleague message) mediator.colleagues
let create_colleague name mediator =
let receive message =
Printf.printf "%s received: %s\n" name message
in
register mediator receive;
receive
let create_colleague_with_state name mediator state =
let receive message =
Printf.printf "%s (state %d) received: %s\n" name state message
in
register mediator receive;
receive
let () =
let mediator = create_mediator () in
let alice = create_colleague "Alice" mediator in
let bob = create_colleague "Bob" mediator in
let charlie = create_colleague "Charlie" mediator in
Printf.printf "Sending messages:\n";
send mediator "Hello everyone!";
send mediator "Meeting at 3pm";
let mediator2 = create_mediator () in
let alice2 = create_colleague_with_state "Alice" mediator2 0 in
let bob2 = create_colleague_with_state "Bob" mediator2 1 in
send mediator2 "Custom message for stateful colleagues"Implement chain of responsibility with linked handlers.
- Handler:
create_handler handle_func - Chain:
set_next handler next_handler - Processing:
handle request - Build:
create_chain handlers
# Chain of Responsibility
type 'a handler = {
handle: 'a -> unit;
set_next: 'a handler -> unit;
mutable next: 'a handler option;
}
let create_handler handle_func =
let next = ref None in
let set_next handler =
next := Some handler
in
let handle request =
handle_func request;
match !next with
| Some handler -> handler.handle request
| None -> ()
in
{ handle; set_next; next = None }
let create_auth_handler () =
create_handler (fun request ->
if Hashtbl.mem request "token" then
Printf.printf "Authentication passed\n"
else
Printf.printf "Authentication failed\n"
)
let create_logger_handler () =
create_handler (fun request ->
let url = try Hashtbl.find request "url" with Not_found -> "unknown" in
Printf.printf "Logging request: %s\n" url
)
let create_validation_handler () =
create_handler (fun request ->
if Hashtbl.mem request "data" then
Printf.printf "Validation passed\n"
else
Printf.printf "Validation failed\n"
)
let create_chain handlers =
match handlers with
| [] -> None
| [h] -> Some h
| h :: rest ->
let rec build acc = function
| [] -> acc
| h2 :: t ->
acc.set_next h2;
build h2 t
in
Some (build h rest)
let () =
let auth = create_auth_handler () in
let logger = create_logger_handler () in
let validator = create_validation_handler () in
auth.set_next logger;
logger.set_next validator;
let request = Hashtbl.create 10 in
Hashtbl.add request "token" "valid";
Hashtbl.add request "url" "/api";
Hashtbl.add request "data" "payload";
Printf.printf "Processing request:\n";
auth.handle request;
let request2 = Hashtbl.create 10 in
Hashtbl.add request2 "url" "/public";
Printf.printf "Processing invalid request:\n";
auth.handle request2Implement state pattern with context and state transitions.
- States:
Ready,Processing,Completed - Context:
create_context - Transition:
handle_state context - Data:
handle_state_with_data
# State pattern
type state =
| Ready
| Processing
| Completed
| Error
type context = {
mutable state: state;
mutable data: string option;
}
let create_context () =
{ state = Ready; data = None }
let transition context new_state =
context.state <- new_state
let handle_state context =
match context.state with
| Ready ->
Printf.printf "Ready: Waiting for input\n";
transition context Processing
| Processing ->
Printf.printf "Processing: Working on task\n";
transition context Completed
| Completed ->
Printf.printf "Completed: Task finished\n";
transition context Ready
| Error ->
Printf.printf "Error: Something went wrong\n";
transition context Ready
let handle_state_with_data context =
match context.state with
| Ready ->
Printf.printf "Ready: Waiting for input\n";
context.data <- Some "Processing started";
transition context Processing
| Processing ->
Printf.printf "Processing: Working on task\n";
context.data <- Some "Processing in progress";
transition context Completed
| Completed ->
Printf.printf "Completed: Task finished\n";
context.data <- Some "Task completed";
transition context Ready
| Error ->
Printf.printf "Error: Something went wrong\n";
context.data <- Some "Error occurred";
transition context Ready
let () =
let context = create_context () in
Printf.printf "Initial state: %s\n" (match context.state with Ready -> "Ready" | Processing -> "Processing" | Completed -> "Completed" | Error -> "Error");
for i = 1 to 5 do
handle_state context;
Printf.printf "State after step %d: %s\n" i (match context.state with Ready -> "Ready" | Processing -> "Processing" | Completed -> "Completed" | Error -> "Error")
done;
Printf.printf "\nWith data:\n";
let context2 = create_context () in
for i = 1 to 5 do
handle_state_with_data context2;
Printf.printf "State: %s, Data: %s\n"
(match context2.state with Ready -> "Ready" | Processing -> "Processing" | Completed -> "Completed" | Error -> "Error")
(match context2.data with Some d -> d | None -> "None")
doneImplement proxy pattern for access control and lazy initialization.
- Real subject:
create_real_subject - Proxy:
create_proxy - Logging:
create_logging_proxy - Auth:
create_auth_proxy
# Proxy pattern
type real_subject = {
request: unit -> string;
}
let create_real_subject () =
{ request = fun () -> "RealSubject: Handling request" }
type proxy = {
real_subject: real_subject option;
request: unit -> string;
}
let create_proxy () =
let real_subject = ref None in
let request () =
match !real_subject with
| None ->
Printf.printf "Proxy: Creating real subject\n";
let subject = create_real_subject () in
real_subject := Some subject;
subject.request ()
| Some subject ->
Printf.printf "Proxy: Using cached real subject\n";
subject.request ()
in
{ real_subject = None; request }
let create_logging_proxy target =
let request () =
Printf.printf "Logging: Request started\n";
let result = target.request () in
Printf.printf "Logging: Request completed\n";
result
in
{ real_subject = None; request }
let create_auth_proxy target =
let request () =
Printf.printf "Auth: Checking permissions\n";
let result = target.request () in
Printf.printf "Auth: Access granted\n";
result
in
{ real_subject = None; request }
let () =
let proxy = create_proxy () in
Printf.printf "First request: %s\n" (proxy.request ());
Printf.printf "Second request: %s\n" (proxy.request ());
let real = create_real_subject () in
let logging_proxy = create_logging_proxy real in
Printf.printf "Logging proxy: %s\n" (logging_proxy.request ());
let auth_proxy = create_auth_proxy real in
Printf.printf "Auth proxy: %s\n" (auth_proxy.request ())Implement flyweight pattern for sharing objects to save memory.
- Flyweight:
create_flyweight shared_state - Factory:
create_flyweight_factory - Get:
get_flyweight factory shared_state - Operation:
operation flyweight unique_state
# Flyweight pattern
type flyweight = {
shared_state: string;
operation: string -> string;
}
let create_flyweight shared_state =
{ shared_state; operation = fun unique_state ->
Printf.sprintf "Shared: %s, Unique: %s" shared_state unique_state
}
type flyweight_factory = {
mutable flyweights: (string, flyweight) Hashtbl.t;
}
let create_flyweight_factory () =
{ flyweights = Hashtbl.create 10 }
let get_flyweight factory shared_state =
if Hashtbl.mem factory.flyweights shared_state then
Hashtbl.find factory.flyweights shared_state
else
let fw = create_flyweight shared_state in
Hashtbl.add factory.flyweights shared_state fw;
fw
let operation flyweight unique_state =
flyweight.operation unique_state
let () =
let factory = create_flyweight_factory () in
let fw1 = get_flyweight factory "state1" in
let fw2 = get_flyweight factory "state1" in
let fw3 = get_flyweight factory "state2" in
Printf.printf "fw1 and fw2 are same: %b\n" (fw1 == fw2);
Printf.printf "fw1 and fw3 are same: %b\n" (fw1 == fw3);
Printf.printf "%s\n" (operation fw1 "unique1");
Printf.printf "%s\n" (operation fw2 "unique2");
Printf.printf "%s\n" (operation fw3 "unique3");
Printf.printf "Number of flyweights: %d\n" (Hashtbl.length factory.flyweights)Implement bridge pattern for separating abstraction from implementation.
- Implementation:
ImplA,ImplB - Abstraction:
create_abstraction impl - Extended:
create_extended_abstraction - Operation:
operation_impl
# Bridge pattern
type implementation =
| ImplA
| ImplB
let operation_impl impl =
match impl with
| ImplA -> "ConcreteImplementationA: Operation"
| ImplB -> "ConcreteImplementationB: Operation"
type abstraction = {
impl: implementation;
operation: unit -> string;
}
let create_abstraction impl =
{ impl; operation = fun () ->
Printf.sprintf "Abstraction: Additional logic - %s" (operation_impl impl)
}
let create_extended_abstraction impl =
{ impl; operation = fun () ->
Printf.sprintf "Extended: More logic - %s" (operation_impl impl)
}
let () =
let abstraction1 = create_abstraction ImplA in
let abstraction2 = create_abstraction ImplB in
let abstraction3 = create_extended_abstraction ImplA in
Printf.printf "%s\n" (abstraction1.operation ());
Printf.printf "%s\n" (abstraction2.operation ());
Printf.printf "%s\n" (abstraction3.operation ());
let abstraction4 = create_abstraction ImplA in
let abstraction5 = create_abstraction ImplB in
Printf.printf "%s\n" (abstraction4.operation ());
Printf.printf "%s\n" (abstraction5.operation ())Implement adapter pattern for converting interfaces.
- Target:
create_target - Adaptee:
create_adaptee - Adapter:
create_adapter adaptee - Logging:
create_adapter_with_logging
# Adapter pattern
type target = {
request: unit -> string;
}
let create_target () =
{ request = fun () -> "Target: Request" }
type adaptee = {
specific_request: unit -> string;
}
let create_adaptee () =
{ specific_request = fun () -> "Adaptee: Specific Request" }
type adapter = {
request: unit -> string;
}
let create_adapter adaptee =
{ request = fun () -> adaptee.specific_request () }
let create_adapters adaptees =
List.map create_adapter adaptees
let create_adapter_with_logging adaptee =
{ request = fun () ->
Printf.printf "Adapter: Logging request\n";
adaptee.specific_request ()
}
let () =
let target = create_target () in
let adaptee = create_adaptee () in
let adapter = create_adapter adaptee in
Printf.printf "%s\n" (target.request ());
Printf.printf "%s\n" (adapter.request ());
let adaptee2 = create_adaptee () in
let adapter_with_logging = create_adapter_with_logging adaptee2 in
Printf.printf "%s\n" (adapter_with_logging.request ());
let adaptees = [create_adaptee (); create_adaptee ()] in
let adapters = create_adapters adaptees in
List.iter (fun a -> Printf.printf "%s\n" (a.request ())) adaptersImplement facade pattern for simplifying complex subsystems.
- Subsystems:
create_subsystem_a,create_subsystem_b - Facade:
create_facade - Simplified:
create_simplified_facade - Operation:
operation,complex_operation
# Facade pattern
type subsystem_a = {
operation: unit -> string;
}
let create_subsystem_a () =
{ operation = fun () -> "SubsystemA: Operation" }
type subsystem_b = {
operation: unit -> string;
}
let create_subsystem_b () =
{ operation = fun () -> "SubsystemB: Operation" }
type subsystem_c = {
operation: unit -> string;
}
let create_subsystem_c () =
{ operation = fun () -> "SubsystemC: Operation" }
type facade = {
operation: unit -> string;
complex_operation: unit -> string;
}
let create_facade () =
let a = create_subsystem_a () in
let b = create_subsystem_b () in
let c = create_subsystem_c () in
{
operation = fun () -> a.operation ();
complex_operation = fun () ->
Printf.sprintf "%s\n%s\n%s"
(a.operation ()) (b.operation ()) (c.operation ())
}
let create_simplified_facade () =
let facade = create_facade () in
{
operation = facade.operation;
complex_operation = facade.complex_operation;
}
let () =
let facade = create_facade () in
Printf.printf "Simple operation:\n%s\n" (facade.operation ());
Printf.printf "Complex operation:\n%s\n" (facade.complex_operation ());
let simplified = create_simplified_facade () in
Printf.printf "Simplified operation:\n%s\n" (simplified.operation ())Implement composite pattern for tree structures.
- Component:
Leaf,Composite - Add:
add_child composite child - Operation:
operation component - Count:
count_leaves
# Composite pattern
type 'a component =
| Leaf of 'a
| Composite of 'a * 'a component list
let create_leaf name = Leaf name
let create_composite name children = Composite (name, children)
let add_child composite child =
match composite with
| Composite (name, children) -> Composite (name, child :: children)
| _ -> composite
let remove_child composite child =
match composite with
| Composite (name, children) ->
Composite (name, List.filter (fun c -> c != child) children)
| _ -> composite
let rec operation = function
| Leaf name -> Printf.sprintf "Leaf %s: Operation" name
| Composite (name, children) ->
let child_results = List.map operation children in
Printf.sprintf "Composite %s: Operation\n%s" name (String.concat "\n" child_results)
let rec get_children = function
| Leaf _ -> []
| Composite (_, children) -> children
let rec count_leaves = function
| Leaf _ -> 1
| Composite (_, children) -> List.fold_left (fun acc c -> acc + count_leaves c) 0 children
let () =
let leaf1 = create_leaf "A" in
let leaf2 = create_leaf "B" in
let leaf3 = create_leaf "C" in
let leaf4 = create_leaf "D" in
let composite1 = create_composite "Comp1" [leaf1; leaf2] in
let composite2 = create_composite "Comp2" [leaf3; composite1] in
let root = create_composite "Root" [leaf4; composite2] in
Printf.printf "%s\n" (operation root);
Printf.printf "Number of leaves: %d\n" (count_leaves root)Implement visitor pattern for adding operations to objects.
- Visitor:
create_visitor - Elements:
ElementA,ElementB - Accept:
accept element visitor - Counting:
create_counting_visitor
# Visitor pattern
type 'a element =
| ElementA of 'a
| ElementB of 'a
type 'a visitor = {
visit_a: 'a -> string;
visit_b: 'a -> string;
}
let create_visitor () =
{
visit_a = fun data -> Printf.sprintf "Visiting ElementA: %s" data;
visit_b = fun data -> Printf.sprintf "Visiting ElementB: %s" data;
}
let create_counting_visitor () =
let count_a = ref 0 in
let count_b = ref 0 in
{
visit_a = fun data ->
count_a := !count_a + 1;
Printf.sprintf "Visiting ElementA (%d): %s" !count_a data;
visit_b = fun data ->
count_b := !count_b + 1;
Printf.sprintf "Visiting ElementB (%d): %s" !count_b data;
}
let accept element visitor =
match element with
| ElementA data -> visitor.visit_a data
| ElementB data -> visitor.visit_b data
let accept_list elements visitor =
List.map (fun e -> accept e visitor) elements
let create_extended_visitor () =
{
visit_a = fun data -> Printf.sprintf "Extended: %s (A)" data;
visit_b = fun data -> Printf.sprintf "Extended: %s (B)" data;
}
let () =
let elements = [ElementA "Hello"; ElementB "World"; ElementA "OCaml"; ElementB "Visitor"] in
let visitor = create_visitor () in
let counting_visitor = create_counting_visitor () in
let extended_visitor = create_extended_visitor () in
Printf.printf "Using standard visitor:\n";
List.iter (fun e -> Printf.printf "%s\n" (accept e visitor)) elements;
Printf.printf "Using counting visitor:\n";
List.iter (fun e -> Printf.printf "%s\n" (accept e counting_visitor)) elements;
Printf.printf "Using extended visitor:\n";
List.iter (fun e -> Printf.printf "%s\n" (accept e extended_visitor)) elementsImplement iterator pattern for sequential access.
- Iterator:
create_iterator collection - Reverse:
create_reverse_iterator - Filter:
create_filtered_iterator - Skip:
create_skip_iterator
# Iterator pattern
type 'a iterator = {
mutable index: int;
collection: 'a list;
has_next: unit -> bool;
next: unit -> 'a;
}
let create_iterator collection =
let index = ref 0 in
{
index = 0;
collection;
has_next = fun () -> !index < List.length collection;
next = fun () ->
let item = List.nth collection !index in
index := !index + 1;
item
}
let create_reverse_iterator collection =
let index = ref (List.length collection - 1) in
{
index = List.length collection - 1;
collection;
has_next = fun () -> !index >= 0;
next = fun () ->
let item = List.nth collection !index in
index := !index - 1;
item
}
let create_filtered_iterator collection predicate =
let filtered = List.filter predicate collection in
create_iterator filtered
let create_skip_iterator collection n =
let skipped = List.drop n collection in
create_iterator skipped
let iterate iterator =
let results = ref [] in
while iterator.has_next () do
results := iterator.next () :: !results
done;
List.rev !results
let () =
let collection = ["A"; "B"; "C"; "D"; "E"] in
let iterator = create_iterator collection in
Printf.printf "Forward iteration:\n";
while iterator.has_next () do
Printf.printf "%s " (iterator.next ())
done;
print_endline "";
let reverse_iter = create_reverse_iterator collection in
Printf.printf "Reverse iteration:\n";
while reverse_iter.has_next () do
Printf.printf "%s " (reverse_iter.next ())
done;
print_endline "";
let filtered_iter = create_filtered_iterator collection (fun s -> String.length s <= 1) in
Printf.printf "Filtered iteration:\n";
while filtered_iter.has_next () do
Printf.printf "%s " (filtered_iter.next ())
done;
print_endline ""Implement template method with customizable steps.
- Template:
create_template - Method:
template_method template - Default:
create_default_template - Logging:
create_logging_template
# Template Method pattern
type 'a template = {
step1: unit -> string;
step2: unit -> string;
step3: unit -> string;
}
let create_template step1 step2 step3 =
{ step1; step2; step3 }
let template_method template =
Printf.printf "%s\n" (template.step1 ());
Printf.printf "%s\n" (template.step2 ());
Printf.printf "%s\n" (template.step3 ())
let create_default_template () =
{
step1 = fun () -> "Step 1";
step2 = fun () -> "Step 2";
step3 = fun () -> "Step 3";
}
let create_logging_template base_template =
{
step1 = fun () ->
let result = base_template.step1 () in
Printf.printf "Logging: %s\n" result;
result;
step2 = fun () ->
let result = base_template.step2 () in
Printf.printf "Logging: %s\n" result;
result;
step3 = fun () ->
let result = base_template.step3 () in
Printf.printf "Logging: %s\n" result;
result;
}
let create_data_processing_template data =
{
step1 = fun () -> Printf.sprintf "Processing data: %s - Step 1" data;
step2 = fun () -> Printf.sprintf "Processing data: %s - Step 2" data;
step3 = fun () -> Printf.sprintf "Processing data: %s - Step 3" data;
}
let () =
Printf.printf "Using default template:\n";
let default = create_default_template () in
template_method default;
Printf.printf "Using logging template:\n";
let logging = create_logging_template default in
template_method logging;
Printf.printf "Using data processing template:\n";
let data_template = create_data_processing_template "example" in
template_method data_templateImplement builder pattern for constructing complex objects.
- Builder:
create_builder - Director:
create_director - Product:
create_product - Build:
build_minimal,build_full
# Builder pattern
type product = {
mutable parts: string list;
}
let create_product () =
{ parts = [] }
let add_part product part =
product.parts <- part :: product.parts
let list_parts product =
Printf.printf "%s\n" (String.concat ", " (List.rev product.parts))
type builder = {
mutable product: product;
reset: unit -> unit;
build_step_a: unit -> unit;
build_step_b: unit -> unit;
build_step_c: unit -> unit;
get_result: unit -> product;
}
let create_builder () =
let product = ref (create_product ()) in
{
product = !product;
reset = fun () -> product := create_product ();
build_step_a = fun () -> add_part !product "Part A";
build_step_b = fun () -> add_part !product "Part B";
build_step_c = fun () -> add_part !product "Part C";
get_result = fun () -> !product;
}
type director = {
builder: builder;
build_minimal: unit -> unit;
build_full: unit -> unit;
build_custom: string list -> unit;
}
let create_director builder =
{
builder;
build_minimal = fun () ->
builder.build_step_a ();
build_full = fun () ->
builder.build_step_a ();
builder.build_step_b ();
builder.build_step_c ();
build_custom = fun steps ->
builder.reset ();
List.iter (fun step ->
match step with
| "A" -> builder.build_step_a ()
| "B" -> builder.build_step_b ()
| "C" -> builder.build_step_c ()
| _ -> ()
) steps;
}
let () =
let builder = create_builder () in
let director = create_director builder in
Printf.printf "Minimal product:\n";
director.build_minimal ();
list_parts builder.product;
Printf.printf "Full product:\n";
director.build_full ();
list_parts builder.product;
Printf.printf "Custom product:\n";
builder.reset ();
builder.build_step_c ();
builder.build_step_a ();
list_parts builder.product;
Printf.printf "Director custom:\n";
director.build_custom ["C"; "A"; "B"];
list_parts builder.productImplement prototype pattern for cloning objects.
- Prototype:
create_prototype data - Clone:
clone,deep_clone - Mutable:
create_mutable_prototype - Cache:
create_prototype_with_cache
# Prototype pattern
type 'a prototype = {
data: 'a;
clone: unit -> 'a prototype;
deep_clone: unit -> 'a prototype;
}
let create_prototype data =
let clone () = create_prototype data in
let deep_clone () = create_prototype data in
{ data; clone; deep_clone }
let create_mutable_prototype initial_data =
let data = ref initial_data in
let clone () = create_mutable_prototype !data in
let deep_clone () = create_mutable_prototype (List.map (fun x -> x) !data) in
let set_data new_data = data := new_data in
let get_data () = !data in
{ data = (set_data, get_data); clone; deep_clone }
let create_prototype_with_cache data =
let cache = Hashtbl.create 10 in
let clone () =
if Hashtbl.mem cache "clone" then
Hashtbl.find cache "clone"
else
let proto = create_prototype data in
Hashtbl.add cache "clone" proto;
proto
in
let deep_clone () = create_prototype data in
{ data; clone; deep_clone }
let () =
let original = create_prototype { name = "Original"; value = 42 } in
let copy = original.clone () in
let deep_copy = original.deep_clone () in
Printf.printf "Original: %s, %d\n" original.data.name original.data.value;
Printf.printf "Copy: %s, %d\n" copy.data.name copy.data.value;
Printf.printf "Deep copy: %s, %d\n" deep_copy.data.name deep_copy.data.value;
let mutable_proto = create_mutable_prototype [1; 2; 3] in
let (set_data, get_data) = mutable_proto.data in
Printf.printf "Original data: ";
List.iter (fun x -> Printf.printf "%d " x) (get_data ());
print_endline "";
set_data [4; 5; 6];
Printf.printf "Modified data: ";
List.iter (fun x -> Printf.printf "%d " x) (get_data ());
print_endline "";
let cloned_mutable = mutable_proto.clone () in
let (set_data2, get_data2) = cloned_mutable.data in
Printf.printf "Clone data: ";
List.iter (fun x -> Printf.printf "%d " x) (get_data2 ());
print_endline ""