Commit 03a88f9f authored by Alan Bleisch's avatar Alan Bleisch
Browse files

Update section4.ml so start is correct. Previously checked for even after...

Update section4.ml so start is correct. Previously checked for even after seeing even, odd after seeing odd.
parent 5c17afb5
(* CSE 341, Section 4, Completed/Solution Code *)
(* mutual recursion, just since we haven't shown it to you *)
(* can do any finite state machine this way -- this one checks
for strictly alternating even/odd
mutual recursion is often not tail-recursive, but for
finite state machines it is
*)
let rec start xs =
match xs with
| [] -> true
| i::xs' -> if i mod 2 = 0 then saw_even xs' else saw_odd xs'
and saw_even xs =
match xs with
| [] -> true
| i::xs' -> i mod 2 = 0 && saw_odd xs'
and saw_odd xs =
match xs with
| [] -> true
| i::xs' -> i mod 2 <> 0 && saw_even xs'
(* style above is correct, but we could if we really had to
use first-class functions to encode mutual recursion by
having earlier functions take function arguments and then
calling them with later functions
*)
let saw_even2 f xs =
match xs with
| [] -> true
| i::xs' -> i mod 2 <> 0 && f xs'
let rec saw_odd2 xs =
match xs with
| [] -> true
| i::xs' -> i mod 2 = 0 && saw_even2 saw_odd2 xs'
let start2 xs =
match xs with
| [] -> true
| i::xs' -> if i mod 2 = 0 then saw_even2 saw_odd2 xs' else saw_odd2 xs'
(* module system *)
(* NO CODE CAN DEPEND ON ANYTHING NOT IN THE MODULE TYPE! *)
module type NONEGINT = sig
type t
val mknni : int -> t option
val add : t -> t -> t (* why not t -> t -> int or t -> t -> t option? *)
val mul : t -> t -> t
val sub : t -> t -> t option
val to_int : t -> int (* why not t -> t ? *)
end
(* this example, thanks to the abstract type in NONEGINT, makes it impossible for a client
to make a value of type NonNegInt.t that is negative.
However, this is assuming (wrongly) that ints do not overflow. That assumption is useful
for a simple example that conveys the idea of relying on an abstraction, but in practice
due to overflow, we would need add and mul to check for negatives and return an option,
like sub, which makes the example less compelling. *)
module NonNegInt : NONEGINT = struct
type t = int
let mknni i =
if i < 0 then
None
else
Some i
let add a b =
a + b (* why not mknni (a * b) ? See comment above. *)
let mul a b =
a * b (* why not mknni (a * b) ? See comment above. *)
let sub a b =
mknni (a - b) (* why not a - b ? *)
let to_int a = a (* external world doesn't know this "is it" *)
end
(* This example shows three different ways to implement the same abstraction where,
thanks to the abstract type, all three behave exactly the same for all clients.
Each uses a different data structure and therefore has different algorithms and
invariants.
*)
module type NONEMPTYLIST = sig
type 'a t
val single : 'a -> 'a t
val cons : 'a -> 'a t -> 'a t
val tl : 'a t -> 'a t
val hd : 'a t -> 'a
val len : 'a t -> int
val map : ('a -> 'b) -> 'a t -> 'b t
end
module Nel_A : NONEMPTYLIST = struct
type 'a t =
| Single of 'a
| Cons of 'a * 'a t
let single x = Single x
let cons x xs = Cons (x,xs)
let tl xs =
match xs with
| Single _ -> failwith "tl of one-element list"
| Cons(_,xs') -> xs'
let hd xs =
match xs with
| Single x -> x
| Cons(x,_) -> x
let rec len xs =
match xs with
| Single _ -> 1
| Cons(_,xs') -> 1 + len xs'
let rec map f xs =
match xs with
| Single x -> Single (f x)
| Cons (x,xs') -> Cons(f x, map f xs')
end
module Nel_B : NONEMPTYLIST = struct
type 'a t = 'a * ('a list)
let single x = (x,[])
let cons x (y,xs) = (x,y::xs)
let tl xs =
match xs with
| (_,[]) -> failwith "tl of one-element list"
| (_,y::ys) -> (y,ys)
let hd = fst
let len (_,xs') =
1 + List.length xs'
let map f (x,xs') =
(f x, List.map f xs')
end
module Nel_C : NONEMPTYLIST = struct
type 'a t = 'a list
let single x = [x]
let cons x xs = x::xs
let tl xs =
match xs with
| [] -> failwith "impossible -- can never happen"
| x::[] -> failwith "tl of one-element list"
| x::xs' -> xs'
let hd = List.hd
let len = List.length
let map = List.map
end
(* CSE 341, Section 4, Completed/Solution Code *)
(* mutual recursion, just since we haven't shown it to you *)
(* can do any finite state machine this way -- this one checks
for strictly alternating even/odd
mutual recursion is often not tail-recursive, but for
finite state machines it is
*)
let rec start xs =
match xs with
| [] -> true
| i::xs' -> if i mod 2 = 0 then saw_even xs' else saw_odd xs'
and saw_even xs =
match xs with
| [] -> true
| i::xs' -> i mod 2 <> 0 && saw_odd xs'
and saw_odd xs =
match xs with
| [] -> true
| i::xs' -> i mod 2 = 0 && saw_even xs'
(* style above is correct, but we could if we really had to
use first-class functions to encode mutual recursion by
having earlier functions take function arguments and then
calling them with later functions
*)
let saw_even2 f xs =
match xs with
| [] -> true
| i::xs' -> i mod 2 <> 0 && f xs'
let rec saw_odd2 xs =
match xs with
| [] -> true
| i::xs' -> i mod 2 = 0 && saw_even2 saw_odd2 xs'
let start2 xs =
match xs with
| [] -> true
| i::xs' -> if i mod 2 = 0 then saw_even2 saw_odd2 xs' else saw_odd2 xs'
(* module system *)
(* NO CODE CAN DEPEND ON ANYTHING NOT IN THE MODULE TYPE! *)
module type NONEGINT = sig
type t
val mknni : int -> t option
val add : t -> t -> t (* why not t -> t -> int or t -> t -> t option? *)
val mul : t -> t -> t
val sub : t -> t -> t option
val to_int : t -> int (* why not t -> t ? *)
end
(* this example, thanks to the abstract type in NONEGINT, makes it impossible for a client
to make a value of type NonNegInt.t that is negative.
However, this is assuming (wrongly) that ints do not overflow. That assumption is useful
for a simple example that conveys the idea of relying on an abstraction, but in practice
due to overflow, we would need add and mul to check for negatives and return an option,
like sub, which makes the example less compelling. *)
module NonNegInt : NONEGINT = struct
type t = int
let mknni i =
if i < 0 then
None
else
Some i
let add a b =
a + b (* why not mknni (a * b) ? See comment above. *)
let mul a b =
a * b (* why not mknni (a * b) ? See comment above. *)
let sub a b =
mknni (a - b) (* why not a - b ? *)
let to_int a = a (* external world doesn't know this "is it" *)
end
(* This example shows three different ways to implement the same abstraction where,
thanks to the abstract type, all three behave exactly the same for all clients.
Each uses a different data structure and therefore has different algorithms and
invariants.
*)
module type NONEMPTYLIST = sig
type 'a t
val single : 'a -> 'a t
val cons : 'a -> 'a t -> 'a t
val tl : 'a t -> 'a t
val hd : 'a t -> 'a
val len : 'a t -> int
val map : ('a -> 'b) -> 'a t -> 'b t
end
module Nel_A : NONEMPTYLIST = struct
type 'a t =
| Single of 'a
| Cons of 'a * 'a t
let single x = Single x
let cons x xs = Cons (x,xs)
let tl xs =
match xs with
| Single _ -> failwith "tl of one-element list"
| Cons(_,xs') -> xs'
let hd xs =
match xs with
| Single x -> x
| Cons(x,_) -> x
let rec len xs =
match xs with
| Single _ -> 1
| Cons(_,xs') -> 1 + len xs'
let rec map f xs =
match xs with
| Single x -> Single (f x)
| Cons (x,xs') -> Cons(f x, map f xs')
end
module Nel_B : NONEMPTYLIST = struct
type 'a t = 'a * ('a list)
let single x = (x,[])
let cons x (y,xs) = (x,y::xs)
let tl xs =
match xs with
| (_,[]) -> failwith "tl of one-element list"
| (_,y::ys) -> (y,ys)
let hd = fst
let len (_,xs') =
1 + List.length xs'
let map f (x,xs') =
(f x, List.map f xs')
end
module Nel_C : NONEMPTYLIST = struct
type 'a t = 'a list
let single x = [x]
let cons x xs = x::xs
let tl xs =
match xs with
| [] -> failwith "impossible -- can never happen"
| x::[] -> failwith "tl of one-element list"
| x::xs' -> xs'
let hd = List.hd
let len = List.length
let map = List.map
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment