Exercises
Association Lists
In this first simple exercise, we will implement a polymorphic
abstract type for association lists, and present two different views
of the implementation.
- Define a signature ALIST
declaring an abstract type with two type parameters (one for the keys,
the other for the associated values), a creation function, an add
function, a lookup function, a membership test, and a deletion
function.
module type ALIST =
sig
type ('a,'b) t
val create : unit -> ('a,'b) t
val add : 'a -> 'b -> ('a,'b) t -> ('a,'b) t
val get : 'a -> ('a,'b) t -> 'b
val mem : 'a -> ('a,'b) t -> bool
val rem : 'a -> ('a,'b) t -> ('a,'b) t
end ;;
Characters 142-148:
Syntax error
The interface should be functional, i.e. without in-place modifications of the abstract type.
- Define a module Alist implementing the
signature ALIST
# module Alist:ALIST =
struct
type ('a,'b) t = ('a*'b) list
let create () = []
let add k v al = (k,v)::al
let get = List.assoc
let mem = List.mem_assoc
let rem = List.remove_assoc
end ;;
Characters 14-19:
Unbound module type ALIST
- Define a signature ADM_ALIST
for ``administrators'' of association lists. Administrators can only
create association lists, and add or remove entries from a list.
# module type ADM_ALIST =
sig
type ('a,'b) t
val create : unit -> ('a,'b) t
val add : 'a -> 'b -> ('a,'b) t -> ('a,'b) t
val rem : 'a -> ('a,'b) t -> ('a,'b) t
end ;;
module type ADM_ALIST =
sig
type ('a, 'b) t
val create : unit -> ('a, 'b) t
val add : 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t
val rem : 'a -> ('a, 'b) t -> ('a, 'b) t
end
- Define a signature USER_ALIST
for ``users'' of association lists. Users can only perform lookups
and membership tests.
# module type USER_ALIST =
sig
type ('a,'b) t
val get : 'a -> ('a,'b) t -> 'b
val mem : 'a -> ('a,'b) t -> bool
end ;;
module type USER_ALIST =
sig
type ('a, 'b) t
val get : 'a -> ('a, 'b) t -> 'b
val mem : 'a -> ('a, 'b) t -> bool
end
- Define two modules AdmAlist and UserAlist
for administrators and for users. Keep in mind that users must be
able to access lists created by administrators.
# module AdmAlist = (Alist:ADM_ALIST with type ('a,'b) t = ('a,'b) Alist.t) ;;
Characters 20-25:
Unbound module Alist
# module UserAlist = (Alist:USER_ALIST with type ('a,'b) t = ('a,'b) Alist.t) ;;
Characters 20-25:
Unbound module Alist
Parameterized Vectors
This exercise illustrates the genericity and code reuse abilities of
parameterized modules. We will define a functor for manipulating
two-dimensional vectors (pairs of (x,y) coordinates) that can be
instantiated with different types for the coordinates.
Numbers have the following signature:
# module type NUMBER =
sig
type a
type t
val create : a -> t
val add : t -> t -> t
val string_of : t -> string
end ;;
-
Define the functor FVector,
parameterized by a module of signature NUMBER,
and defining a type t of two-dimensional vectors over these numbers,
a creation function, an addition function, and a conversion to strings.
# module FVector (N:NUMBER) =
struct
type e = N.t
type t = { mutable vx:e; mutable vy:e }
let create x0 y0 = { vx=x0; vy=y0 }
let add v1 v2= {vx = N.add v1.vx v2.vx; vy = N.add v1.vy v2.vy}
let string_of v= "("^N.string_of v.vx^" , "^N.string_of v.vy^")"
end ;;
module FVector :
functor(N : NUMBER) ->
sig
type e = N.t
and t = { mutable vx: e; mutable vy: e }
val create : e -> e -> t
val add : t -> t -> t
val string_of : t -> string
end
- Define a signature VECTOR,
without parameters, where the types of numbers and vectors are abstract.
# module type VECTOR =
sig
type e
type t
val create : e -> e -> t
val add :t -> t -> t
val string_of : t -> string
end ;;
module type VECTOR =
sig
type e
and t
val create : e -> e -> t
val add : t -> t -> t
val string_of : t -> string
end
- Define three structures Rational,
Float et Complex implementing the signature
NUMBER.
# module Rational:NUMBER =
struct
type a = int*int
type t = { p:int; q:int }
let create (p0,q0) = { p=p0; q=q0 }
let add r1 r2 = { p = r1.p*r2.q + r2.p*r1.q; q = r1.q*r2.q }
let string_of r = (string_of_int r.p)^"/"^(string_of_int r.q)
end ;;
module Rational : NUMBER
# module Float:NUMBER =
struct
type a = float
type t = a
let create x = x
let add = (+.)
let string_of = string_of_float
end ;;
module Float : NUMBER
# module Complex:NUMBER =
struct
type a = float*float
type t = { r:float; i:float }
let create (r0, i0) = { r=r0; i=i0 }
let add c1 c2 = { r = c1.r+.c2.r; i = c1.i+.c2.i }
let string_of c =
(string_of_float c.r)^"+"^(string_of_float c.r)^"*i"
end ;;
module Complex : NUMBER
- Use these structures to define (by functor application)
three modules for vectors of rationals, reals
and complex.
# module RatVect:VECTOR = FVector(Rational) ;;
module RatVect : VECTOR
# module FloVect:VECTOR = FVector(Float) ;;
module FloVect : VECTOR
# module ComVect:VECTOR = FVector(Complex) ;;
module ComVect : VECTOR
Lexical Trees
This exercise follows up on the lexical trees introduced in
chapter 2, page ??. The goal is to
define a generic module for handling lexical trees, parameterized by
an abstract type of words.
-
Define the signature WORD defining
an abstract type alpha for letters of the alphabet,
and another abstract type t for words on this alphabet.
Declare also the empty word, the conversion from an alphabet letter to
a one-letter word, the accessor to a letter of a word, the sub-word
operation, the length of a word, and word concatenation.
# module type WORD =
sig
type alpha
type t
val null : t
val of_alpha : alpha -> t
val get : t -> int -> alpha
val sub : t -> int -> int -> t
val length : t -> int
val concat : t -> t -> t
end ;;
module type WORD =
sig
type alpha
and t
val null : t
val of_alpha : alpha -> t
val get : t -> int -> alpha
val sub : t -> int -> int -> t
val length : t -> int
val concat : t -> t -> t
end
- Define the functor LexTree,
parameterized by a module implementing WORD, that defines
(as a function of the types and operations over words) the type of
lexical trees and functions exists, insert et
select similar to those from chapter 2, page
??.
# module LexTree (W:WORD) =
struct
type node = Letter of W.alpha * bool * t
and t = node list
let rec exist m d =
let aux sm i n =
match d with
[] -> false
| (Letter (a,b,l))::q when a = W.get sm i ->
if n = 1 then b else exist (W.sub sm (i+1) (n-1)) l
| (Letter (a,b,l))::q when a = W.get sm i ->
exist sm q
in aux m 0 (W.length m)
let rec insert m d =
let aux sm i n =
if n = 0 then d
else
match d with
[] ->
let aux = insert (W.sub sm (i+1) (n-1)) [] in
[ Letter (W.get sm i, n = 1, aux) ]
| (Letter(a,b,l))::q when a = W.get sm i ->
if n = 1 then (Letter(a,true,l))::q
else Letter(a,b,add (W.sub sm (i+1) (n-1)) l)::q
| (Letter(a,b,l))::q -> (Letter(a,b,l))::(ajoute sm q)
in aux m 0 (W.length m)
let rec select n d = match d with
[] -> []
| (Letter(a,b,l))::q when n=1 ->
let f (Letter(a,b,_)) = if b then W.of_alpha a else W.null in
List.filter ((<>) W.null) (List.map f d)
| (Letter(a,b,l))::q ->
let r = select (n-1) l and r2 = select n q in
let pr = List.map (function s -> W.concat (W.of_alpha a) s) r in
pr@r2
end ;;
Characters 161-407:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
_::_
Characters 853-854:
This expression has type t = node list but is here used with type W.t dlist
- Define the module Chars implementing the WORD
signature for the types alpha = char and t = string.
Use it to obtain a module CharDict
implementing dictionaries whose keys are character strings.
# module Chars =
struct
type alpha = char
type t = string
let null = ""
let of_alpha c = String.make 1 c
let get s i =
try s.[i]
with Invalid_argument(_) -> raise (Invalid_argument "Chars.get")
let sub s i1 i2 =
try String.sub s i1 i2
with Invalid_argument(_) -> raise (Invalid_argument "Chars.sub")
let length = String.length
let concat = (^)
end ;;
module Chars :
sig
type alpha = char
and t = string
val null : string
val of_alpha : char -> string
val get : string -> int -> char
val sub : string -> int -> int -> string
val length : string -> int
val concat : string -> string -> string
end
# module CharsDic = LexTree(Chars) ;;
Characters 19-26:
Unbound module LexTree