...
 
Commits (4)
(* 7.5 type definitions *)
type rat = int * int (* num, denom *)
type var = string (* new *)
type binary_op = Add | Sub | Mul | Div
type unary_op = Neg
type expr = Const of rat
| UnOp of unary_op * expr
| BinOp of binary_op * expr * expr
| Var of var (* new *)
| Func of var * expr (* new *)
| Bind of var * expr * expr (* new *)
| App of expr * expr (* new *)
| Ite of expr * expr * expr (* new *)
type value = Rat of rat | Fun of var * state * expr (* new *)
and state = var -> value option (* new *)
(* 7.6 type definitions *)
type graph = (int * float * int) list
(*****************************************************************************)
(**************************** HOMEWORK STARTS HERE ***************************)
(*****************************************************************************)
(* Assignment 7.4 [7 points] *)
let f1 acc _ = acc + 1
let f2 acc l = if List.length l > List.length acc then l else acc
let f3 acc (a,b) = acc @ [b,a]
let f4 acc x = x :: List.rev acc
let f5 acc (k,v) = fun x -> if x = k then v else acc x
let f6 acc f = (f (List.hd acc))::acc
let f7 acc n = acc * acc * n
(* Assignment 7.5 [7 points] *)
let rec eval_expr (s : state) (e : expr) : value =
match e with Const c -> Rat c
| UnOp (Neg, e) -> (match eval_expr s e with
| Rat (n, d) -> Rat (-n, d)
| _ -> failwith "invalid type")
| BinOp (op, e1, e2) ->
(match eval_expr s e1, eval_expr s e2 with
| Rat (n1, d1), Rat (n2, d2) ->
(match op with
| Add -> Rat (n1*d2+n2*d1,d1*d2)
| Sub -> Rat (n1*d2-n2*d1,d1*d2)
| Mul -> Rat (n1*n2,d1*d2)
| Div -> Rat (n1*d2,d1*n2))
| _ -> failwith "invalid type")
(* TODO: continue here *)
| Var v -> (match s v with Some x -> x
| None -> failwith "unknown variable")
| Bind (v, e, b) -> let x = eval_expr s e in
eval_expr (fun r -> if r = v then Some x else s r) b
| Func (a, b) -> Fun (a, s, b)
| App (f, a) -> let v = eval_expr s a in
(match eval_expr s f with Fun (x, s', b) ->
eval_expr (fun r -> if r = x then Some v else s' r) b
| _ -> failwith "not a function")
| Ite (c, t, e) -> (match eval_expr s c with
| Rat (0, _) -> eval_expr s e
| Rat (_, _) -> eval_expr s t
| _ -> failwith "invalid type")
(*****************************************************************************)
(* assignment 7.6 [6 points] *)
let mst g =
let rec main t visited edges =
if edges = [] then t else
let (v_to_uv, edges) = List.partition (fun (s,_,_) -> List.find_opt ((=) s) visited <> None) edges in
let (uv_to_v, edges) = List.partition (fun (_,_,s) -> List.find_opt ((=) s) visited <> None) edges in
let border_edges = v_to_uv @ (List.map (fun (s,w,d) -> d,w,s) uv_to_v) in
let min_e = List.fold_left (fun (ms,mw,md) (s,w,d) -> if w < mw then s,w,d else ms,mw,md) (0,infinity,0) border_edges in
let _,_,min_d = min_e in
let edges = edges @ List.filter (fun (_,_,d) -> d <> min_d) border_edges in
main (min_e::t) (min_d::visited) edges
in
main [] [0] g
(*****************************************************************************)
(***************************** HOMEWORK ENDS HERE ****************************)
(*****************************************************************************)
(* example inputs, you may use them to test your implementations,
but [do not change] *)
(*
_a + _b
*)
let a75_ex1 = BinOp (Add, Var "_a", Var "_b")
(*
let x = 2/7 in x
*)
let a75_ex2 = Bind ("x", Const (2, 7), Var "x")
(*
if 3/4 then 1/2 else 2/1
*)
let a75_ex3 = Ite (Const (3, 4), Const (1, 2), Const (2, 1))
(*
if 1/4 * 0/1 then 1/2 else 2/1
*)
let a75_ex4 = Ite (BinOp (Mul, Const (1, 4), Const (0, 1)), Const (1, 2), Const (2, 1))
(*
(fun z -> 1/3 - z)
*)
let a75_ex5 = Func ("z", BinOp (Sub, Const (1, 3), Var "z"))
(*
let f = fun x -> x * x in
f (1/2 - 1/4)
*)
let a75_ex6 = Bind ("f", Func ("x", BinOp (Mul, Var "x", Var "x")), App (Var "f", BinOp (Sub, Const (1, 2), Const (1, 4))))
(*
let add = fun x -> fun y -> x + y in
let x = 2/3 in
add x 4/3
*)
let a75_ex7 = Bind ("add", Func ("x", Func ("y", BinOp (Add, Var "x", Var "y"))), Bind ("x", Const (2,3), App (App (Var "add", Var "x"), Const (4, 3))))
(*
(let x = 2/1 in fun a -> let x = a + x in x * x) 10/2
*)
let a75_ex8 = App (Bind ("x", Const (2,1), Func ("a", Bind ("x", BinOp (Add, Var "a", Var "x"), BinOp (Mul, Var "x", Var "x")))), Const (10, 2))
let a76_ex1 = [0,1.,1; 0,4.,2; 1,2.,2; 1,1.,3; 2,3.,3]
let a76_ex2 = [0,4.,1; 0,4.,7; 1,8.,2; 1,11.,7; 2,7.,3; 2,4.,5; 2,2.,8; 3,9.,4; 3,14.,5; 4,10.,5; 5,2.,6; 6,1.,7; 6,6.,8; 7,7.,8;]
(*****************************************************************************)
(* TESTS [do not change] *)
let (=.) a b = (abs_float (a -. b)) < 0.001
let print_mst_for t =
print_endline ("[" ^ (String.concat "; " (List.map (fun (s,w,d) -> "(" ^ (string_of_int s) ^ "," ^ (string_of_float w) ^ "," ^ (string_of_int d) ^ ")") t)) ^ "]")
let simp (n,d) =
let k, n = if n < 0 then -1, -n else 1, n in
let k, d = if d < 0 then -k, -d else k, d in
let rec gcd a b =
if b = 0 then a else gcd b (a mod b)
in
let g = gcd n d in
(k * n / g, d / g)
let test_ee p e =
let s = fun r -> if r = "_a" then Some (Rat (1,3)) else if r = "_b" then Some (Rat (1, 6)) else Some (Rat (0, 1)) in
match e, eval_expr s p with
| Rat (en, ed), Rat (pn, pd) -> (en,ed) = simp (pn, pd)
| Fun (ea, _, ed), Fun (pa, _, pd) -> ea = pa && ed = pd
| _, _ -> false
let test_mst g e =
let sort_t l = List.map (fun (s,w,d) -> if d < s then (d, w, s) else (s, w, d)) l |>
List.sort (fun (s1,_,d1) (s2,_,d2) -> if s1 = s2 then compare d1 d2 else compare s1 s2) in
let t1 = (sort_t (mst g)) in
let t2 = (sort_t e) in
let rec cmp l1 l2 = match l1, l2 with [],[] -> true
| (s1,w1,d1)::xs, (s2,w2,d2)::ys -> s1 = s2 && d1 = d2 && w1 =. w2 && cmp xs ys
| _, _ -> false
in
(* print_mst_for t1; print_mst_for t2; *)
cmp t1 t2
let tests = [
(* tests for 7.4 *)
__LINE_OF__ (fun () -> (List.fold_left f1 0 ["ab"; "xx"; "ab"; "u"; "iuw"; "bb"]) = 6);
__LINE_OF__ (fun () -> (List.fold_left f2 [] [[1;2;3]; [0]; [7;9;1;3]; []; [1]]) = [7;9;1;3]);
__LINE_OF__ (fun () -> (List.fold_left f3 [] [(1,2); (3,4); (5,6)]) = [(2,1); (4,3); (6,5)]);
__LINE_OF__ (fun () -> (List.fold_left f4 [] ['a';'b';'c';'d';'e';'f';'g']) = ['g';'e';'c';'a';'b';'d';'f']);
__LINE_OF__ (fun () -> let g = List.fold_left f5 (fun _ -> 0) [('a',3); ('z', -9); ('d', 18)] in (g 'a' = 3) && (g 'd' = 18) && (g 'z' = -9));
__LINE_OF__ (fun () -> (List.fold_left f6 [0] [(fun x -> x + 3); (fun x -> x * x); (fun x -> x * -4)]) = [-36;9;3;0]);
__LINE_OF__ (fun () -> (List.fold_left f7 (-3) [6;-2;3]) = 102036672);
(* tests for 7.5 *)
__LINE_OF__ (fun () -> test_ee a75_ex1 (Rat (1, 2)));
__LINE_OF__ (fun () -> test_ee a75_ex2 (Rat (2, 7)));
__LINE_OF__ (fun () -> test_ee a75_ex3 (Rat (1, 2)));
__LINE_OF__ (fun () -> test_ee a75_ex4 (Rat (2, 1)));
__LINE_OF__ (fun () -> test_ee a75_ex5 (Fun ("z", (fun _ -> None), BinOp (Sub, Const (1, 3), Var "z"))));
__LINE_OF__ (fun () -> test_ee a75_ex6 (Rat (1, 16)));
__LINE_OF__ (fun () -> test_ee a75_ex7 (Rat (2, 1)));
__LINE_OF__ (fun () -> test_ee a75_ex8 (Rat (49, 1)));
(* tests for 7.6 *)
__LINE_OF__ (fun () -> test_mst a76_ex1 [0,1.,1; 1,2.,2; 1,1.,3]);
__LINE_OF__ (fun () -> test_mst a76_ex2 [0,4.,1; 0,4.,7; 2,7.,3; 2,4.,5; 2,2.,8; 3,9.,4; 5,2.,6; 6,1.,7]);
]
let () =
let rec input_lines ch =
(try Some (input_line ch) with _ -> None) (* catch stupid EOF exception *)
|> function Some line -> line :: input_lines ch | None -> []
in
let lines = input_lines (open_in __FILE__) in
let open List in
let open Printf in
let fail l =
let line = nth lines (l-1) in
let test = String.sub line 25 (String.length line - 27) in
printf "test \027[31;m%s\027[0;m (line %d) failed!\n" test l;
in
let test (l, t) =
let ok = try t () with e -> print_endline (Printexc.to_string e); false in
if not ok then fail l;
ok
in
let passed = filter (fun x -> x) (map test tests) in
printf "passed %d/%d tests\n" (length passed) (length tests)
soccer shoes:83
superman action doll set:50
superman action doll set:52
bike:32
guitar:18
barbie's dream house:12
karaoke machine:51
horse:80
ocaml book:1
unicorn:90
socks:41
colorful pencils:16
penguin doll:99
guinea pig:53
soccer shoes:83
superman action doll set:50
superman action doll set:52
bike:32
guitar:18
barbie's dream house:12
karaoke machine:51
horse:80
unicorn:90
socks:41
colorful pencils:16
penguin doll:99
guinea pig:53
ocaml book:10
horse:3
colorful pencils:12
ocaml book:8
time machine:13
unicorn:3
shawl:5
penguin doll:1
ocaml book:7
soccer shoes:1
socks:3
tommy:nice
bruno:naughty
frida:nice
caren:nice
marta:naughty
bruno:nice
caren:ngty
marta:nice
bike:10
sand toys:20
soccer shoes:8
colorful pencils:1
ocaml book:3
superman action doll set:17
guinea pig:13
guitar:12
soccer shoes:3
penguin doll:1
ocaml book:2
time machine:53
bike:7
barbie's dream house:5
guitar:6
colorful pencils:2
socks:1
shawl:2
karaoke machine:13
superman action doll set:3
guinea pig:3
horse:10
unicorn:8
sand toys:4
soccer shoes:3
shawl:2
karaoke machine:13
horse
unicorn:8
soccer shoes:3
horse:10
unicorn:sand toys
soccer shoes:3
guitar:6
:2
superman action doll set:3
guinea pig:3
horse:3
ocaml book
time machine:12
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assignment 9.1 (P) (Delayed) evaluation, unit, side-effects, pure functions\n",
"\n",
"We can see let-bindings as functions without any arguments. Therefore they can and will be immediately evaluated.\n",
"If we want to delay evaluation of the bound expression until application, we can introduce an argument. However, if there are no free variables in our expression, we just need some dummy argument. To indicate this, we use the type `unit` which has only one value `()` (which can be seen as the empty tuple).\n",
"\n",
"Discuss this difference between the following two expressions"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"let x = print_endline \"foo\" in x, x"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"let x () = print_endline \"foo\" in x (), x ()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"1. What are side-effects? Give some examples.\n",
"2. What are pure functions? What are their benefits?\n",
"3. Why does delaying evaluation only make sense in case of side-effects?\n",
"4. Why do we want to use `()` instead of some unused variable?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assignment 9.2 (P) Students in, students out!\n",
"\n",
"Once again, we consider our student records:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"type student = {\n",
" first_name : string;\n",
" last_name : string;\n",
" id : int;\n",
" semester : int;\n",
" grades : (int * float) list\n",
"}\n",
"\n",
"type database = student list"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we define a file format to store students that, for each student, contains a line \n",
"\n",
"<i>first_name</i>;<i>last_name</i>;<i>id</i>;<i>semester</i>;<i>gc</i>\n",
"\n",
"where <i>gc</i> number of lines\n",
"\n",
"<i>course</i>;<i>grade</i>\n",
"\n",
"follow with grades.\n",
"\n",
"1) Implement a function `load_db : string -> database` to load the students from the given file. Throw an exception `Corrupt_database_file` if something is wrong with the file.\n",
"\n",
"2) Implement a function `store_db : string -> database -> unit` to store the students back to the given file."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "OCaml 4.07.0",
"language": "OCaml",
"name": "ocaml-jupyter"
},
"language_info": {
"codemirror_mode": "text/x-ocaml",
"file_extension": ".ml",
"mimetype": "text/x-ocaml",
"name": "OCaml",
"nbconverter_exporter": null,
"pygments_lexer": "OCaml",
"version": "4.07.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assignment 9.1 (P) (Delayed) evaluation, unit, side-effects, pure functions\n",
"\n",
"We can see let-bindings as functions without any arguments. Therefore they can and will be immediately evaluated.\n",
"If we want to delay evaluation of the bound expression until application, we can introduce an argument. However, if there are no free variables in our expression, we just need some dummy argument. To indicate this, we use the type `unit` which has only one value `()` (which can be seen as the empty tuple).\n",
"\n",
"Discuss this difference between the following two expressions"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"let x = print_endline \"foo\" in x, x"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"let x () = print_endline \"foo\" in x (), x ()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"1. What are side-effects? Give some examples.\n",
"2. What are pure functions? What are their benefits?\n",
"3. Why does delaying evaluation only make sense in case of side-effects?\n",
"4. Why do we want to use `()` instead of some unused variable?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"1. Side-effects interact with the environment during evaluation. This means writing data without contributing to the returned value or reading data without depending on the arguments. E.g. writing/reading to/from some channel (e.g. stdout, files, network), changing/reading some data outside the function (mutable data structures, e.g. references, hashtables, arrays), reading time, getting random values etc.\n",
"2. Pure functions are functions without observable side-effects (we ignore the effects of computation itself (e.g. used memory, caches, spent CPU cycles)). Like functions in the mathematical sense, their return value will only depend on the values of the arguments and will therefore always give the same result for some input. Reproducible results facilitate testing, parallization (no interaction between different calls), memoization (we can save the result for some input and don't need to compute it again), and on-demand/lazy evaluation (if there are no side-effects, we only need to evaluate some function if we depend on its value).\n",
"3. If we have all needed arguments for application and the function has no side-effects, it does not matter when we evaluate it, since the call will always give the same value and not influence anything else. You could argue that even for a pure function it does make a difference when you evaluate if it does not terminate, but this will only make a difference if there are side-effects in the program (i.e. which side-effects are executed before the non-terminating call).\n",
"4. The only way to convey in the signature (besides some agreed upon convention) that the evaluation is not influenced by an argument, is by limiting its values to exactly one value. If we see a signature `'a -> int` it could for example compute some hash of the first argument (only possible because of the built-in polymorphic functions depending on runtime representation); for `unit -> int` we know that it will either be a constant function or get the `int` via side-effect; for `unit -> unit` we know that the function will only do something via side-effects (or do nothing). The choice of `unit` to indicate side-effects for both arguments and results is rather arbitrary. We could also define something like the following to further classify side-effects:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"type resource = Terminal | File | Network | Mutable\n",
"type action = Read | Write\n",
"type side_effect = action * resource\n",
"let print_endline' x : side_effect = print_endline x; Write, Terminal\n",
"let _ =\n",
" print_endline' \"foo\" ::\n",
" print_endline' \"bar\" ::\n",
" []"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assignment 9.2 (P) Students in, students out!\n",
"\n",
"Once again, we consider our student records:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"type student = {\n",
" first_name : string;\n",
" last_name : string;\n",
" id : int;\n",
" semester : int;\n",
" grades : (int * float) list\n",
"}\n",
"\n",
"type database = student list"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we define a file format to store students that, for each student, contains a line \n",
"\n",
"<i>first_name</i>;<i>last_name</i>;<i>id</i>;<i>semester</i>;<i>gc</i>\n",
"\n",
"where <i>gc</i> number of lines\n",
"\n",
"<i>course</i>;<i>grade</i>\n",
"\n",
"follow with grades.\n",
"\n",
"1) Implement a function `load_db : string -> database` to load the students from the given file. Throw an exception `Corrupt_database_file` if something is wrong with the file.\n",
"\n",
"2) Implement a function `store_db : string -> database -> unit` to store the students back to the given file."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"(* find the solution in the file p09_sol.ml *)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "OCaml 4.07.0",
"language": "OCaml",
"name": "ocaml-jupyter"
},
"language_info": {
"codemirror_mode": "text/x-ocaml",
"file_extension": ".ml",
"mimetype": "text/x-ocaml",
"name": "OCaml",
"nbconverter_exporter": null,
"pygments_lexer": "OCaml",
"version": "4.07.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
ocaml book:10
horse:3
colorful pencils:12
let todo _ = failwith "TODO"
type behavior = Nice | Naughty
type notes = (string * behavior) list
type selection_alg = (string * int * int) list -> int -> string list
exception Invalid_file_format of string
(* 9.3 - 1 *)
let read_notes = todo
(* 9.3 - 2 *)
let read_wishlist = todo
(* 9.3 - 3 *)
let load_catalogue = todo
(* 9.3 - 4 *)
let write_list = todo
(* 9.3 - 5 *)
let write_letter = todo
(* 9.3 - 6 *)
let run_santas_factory = todo
(* 9.3 - 7 *)
let knapsack = todo
(*****************************************************************************)
(**************************** END OF HOMEWORK ********************************)
(*****************************************************************************)
(* example inputs, you may use them to test your implementations,
but [do not change] *)
let a933_ex1 = ["penguin doll",1; "ocaml book",2; "time machine",53; "bike",7; "barbie's dream house",5;
"guitar",6; "colorful pencils",2; "socks",1; "shawl",2; "karaoke machine",13; "superman action doll set",3;
"guinea pig",3; "horse",10; "unicorn",8; "sand toys",4; "soccer shoes",3]
(*****************************************************************************)
(* TESTS [do not change] *)
let (=^) a b =
(List.sort compare a) = (List.sort compare b)
let (=|) a b =
let a = List.sort_uniq (fun x y -> compare (fst x) (fst y)) a in
let b = List.sort_uniq (fun x y -> compare (fst x) (fst y)) b in
a = b
let check_throws e f =
try f (); false with e' -> e' = e
let check_file filename content =
let file = open_in filename in
let rec read acc =
try
read ((input_line file)::acc)
with End_of_file -> acc
in
let c = read [] in
close_in file;
(List.sort_uniq compare c) = (List.sort_uniq compare content)
let check_letter filename =
let file = open_in filename in
let rec read () =
try
let line = input_line file in
if line <> "" then true else
read ()
with End_of_file -> false
in
let r = read () in
close_in file;
r
let raise' = function Failure f ->
Printf.printf "TEST FAILURE: %s\n" f;
raise (Failure f)
| e -> raise e
let check_run_santas_factory () =
let test_selection_alg wishes capacity =
if capacity <> 13 then raise' (Failure "wrong capacity passed to selection_alg");
(match List.find_opt (fun (t,_,_) -> t = "ocaml book") wishes with
| None -> raise' (Failure "wrong list passed to selection_alg")
| Some (_,_,w) -> if w <> 2 then raise' (Failure "wrong list passed to selection_alg"));
match List.sort (fun (_,i,_) (_,j,_) -> compare j i) wishes with
| (w1,_,_)::(w2,_,_)::_ -> [w1;w2]
| _ -> raise' (Failure "wrong list passed to selection_alg")
in
ignore(run_santas_factory 13 test_selection_alg);
if not (check_letter "marta_letter.txt") then raise (Failure "no correct letter produced for marta");
if not (check_letter "bruno_letter.txt") then raise (Failure "no correct letter produced for bruno");
if not (check_file "frida_presents.txt" ["colorful pencils";"ocaml book"]) then raise (Failure "no correct present list produced for frida");
if not (check_file "tommy_presents.txt" ["sand toys";"superman action doll set"]) then raise (Failure "no correct present list produced for tommy");
if not (check_file "caren_presents.txt" ["penguin doll";"unicorn"]) then raise (Failure "no correct present list produced for caren");
true
let tests = [
(* tests for 9.3 - 1 *)
__LINE_OF__ (fun () -> (read_notes "examples/santas_notes.txt") =| ["tommy",Nice;"bruno",Naughty;"frida",Nice;"caren",Nice;"marta",Naughty]);
__LINE_OF__ (fun () -> let fn = "examples/santas_notes_broken1.txt" in check_throws (Invalid_file_format fn) (fun () -> read_notes fn));
__LINE_OF__ (fun () -> let fn = "examples/santas_notes_broken2.txt" in check_throws (Invalid_file_format fn) (fun () -> read_notes fn));
(* tests for 9.3 - 2 *)
__LINE_OF__ (fun () -> (read_wishlist "examples/frida_wishlist.txt") =| ["ocaml book",10;"horse",3;"colorful pencils",12]);
__LINE_OF__ (fun () -> let fn = "examples/wishlist_broken1.txt" in check_throws (Invalid_file_format fn) (fun () -> read_wishlist fn));
__LINE_OF__ (fun () -> let fn = "examples/wishlist_broken2.txt" in check_throws (Invalid_file_format fn) (fun () -> read_wishlist fn));
__LINE_OF__ (fun () -> let fn = "examples/wishlist_broken3.txt" in check_throws (Invalid_file_format fn) (fun () -> read_wishlist fn));
(* tests for 9.3 - 3 *)
__LINE_OF__ (fun () -> (load_catalogue "examples/toys_catalogue.txt") =| a933_ex1);
__LINE_OF__ (fun () -> let fn = "examples/toys_catalogue_broken1.txt" in check_throws (Invalid_file_format fn) (fun () -> load_catalogue fn));
__LINE_OF__ (fun () -> let fn = "examples/toys_catalogue_broken2.txt" in check_throws (Invalid_file_format fn) (fun () -> load_catalogue fn));
__LINE_OF__ (fun () -> let fn = "examples/toys_catalogue_broken3.txt" in check_throws (Invalid_file_format fn) (fun () -> load_catalogue fn));
(* tests for 9.3 - 4 *)
__LINE_OF__ (fun () -> let l = ["socks";"colorful pencils";"horse"] in let fn = "examples/testout_list1.txt" in write_list fn l; check_file fn l);
(* tests for 9.3 - 5 *)
__LINE_OF__ (fun () -> let fn = "examples/testout_letter1.txt" in write_letter fn; check_letter fn);
(* tests for 9.3 - 6 *)
__LINE_OF__ (fun () -> check_run_santas_factory ());
(* tests for 9.3 - 7 *)
__LINE_OF__ (fun () -> (knapsack ["a",5,4; "b",2,2; "b",2,2; "d",4,5; "b",2,2; "e",8,2] 10) =^ ["a";"b";"b";"e"]);
__LINE_OF__ (fun () -> (knapsack ["a",5,4; "a",5,4; "c",11,6; "d",4,5; "e",8,2; "a",5,4] 10) =^ ["c";"e"]);
]
let () =
let rec input_lines ch =
(try Some (input_line ch) with _ -> None) (* catch stupid EOF exception *)
|> function Some line -> line :: input_lines ch | None -> []
in
let lines = input_lines (open_in __FILE__) in
let open List in
let open Printf in
let fail l =
let line = nth lines (l-1) in
let test = String.sub line 25 (String.length line - 27) in
printf "test \027[31;m%s\027[0;m (line %d) failed!\n" test l;
in
let test (l, t) =
let ok = try t () with e -> print_endline (Printexc.to_string e); false in
if not ok then fail l;
ok
in
let passed = filter (fun x -> x) (map test tests) in
printf "passed %d/%d tests\n" (length passed) (length tests)
ocaml book:8
time machine:13
unicorn:3
shawl:5
penguin doll:1
ocaml book:7
soccer shoes:1
socks:3
type student = {
first_name : string;
last_name : string;
id : int;
semester : int;
grades : (int * float) list;
}
type database = student list
exception Corrupt_database_file
let store_db filename db =
let file = open_out filename in
let rec write_grade (c,g) = Printf.fprintf file "%d;%.2f\n" c g
in
let write_student s =
Printf.fprintf file "%s;%s;%d;%d;%d\n" s.first_name s.last_name s.id s.semester (List.length s.grades);
List.iter write_grade s.grades;
in
List.iter write_student db;
close_out file
let load_db filename =
let file = open_in filename in
let rec read_grades gc grades =
if gc <= 0 then List.rev grades else
try
let line = input_line file in
match String.split_on_char ';' line with
| [course_s;grade_s] ->
let course,grade = (try
int_of_string course_s, float_of_string grade_s
with _ -> raise Corrupt_database_file) in
if course < 0 || grade < 1.0 || grade > 5.0 then raise Corrupt_database_file
else read_grades (gc-1) ((course,grade)::grades)
| _ -> raise Corrupt_database_file
with End_of_file -> raise Corrupt_database_file
in
let rec read_students db =
try
let line = input_line file in
match String.split_on_char ';' line with
| ""::_ | _::""::_ -> raise Corrupt_database_file
| [first_name;last_name;id_s;sem_s;gc_s] ->
let id,semester,gc = try
int_of_string id_s, int_of_string sem_s, int_of_string gc_s
with _ -> raise Corrupt_database_file in
if id < 0 || semester < 0 || gc < 0 || gc > 100 then raise Corrupt_database_file
else if List.find_opt (fun s -> s.id = id) db <> None then raise Corrupt_database_file
else
let grades = read_grades gc [] in
read_students ({ first_name; last_name; id; semester; grades }::db)
| _ -> raise Corrupt_database_file
with End_of_file -> db
in
try
let db = read_students [] |> List.rev in
close_in file;
db
with e -> close_in file; raise e
let a55_ex1 = [
{ first_name = "Anton"; last_name = "Maier"; id=173; semester=3; grades=[1, 1.7; 4, 2.3; 18, 3.0] };
{ first_name = "Betty"; last_name = "Schmidt"; id=418; semester=1; grades=[] };
{ first_name = "Carla"; last_name = "Kurz"; id=223; semester=2; grades=[1, 4.0; 3, 1.0; 7, 1.3; 12, 1.0] };
{ first_name = "Denis"; last_name = "Uler"; id=19; semester=3; grades=[1, 2.2; 7, 1.0; 8, 5.0] }
]
tommy:nice
bruno:naughty
frida:nice
caren:nice
marta:naughty
Anton;Maier;173;3;3
1;1.70
4;2.30
18;3.00
Betty;Schmidt;418;1;0
Carla;Kurz;223;2;4
1;4.00
3;1.00
7;1.30
12;1.00
Denis;Uler;19;3;3
1;2.20
7;1.00
8;5.00
bike:10
sand toys:20
soccer shoes:8
colorful pencils:1
ocaml book:3
superman action doll set:17
guinea pig:13
guitar:12
soccer shoes:3
penguin doll:1
ocaml book:2
time machine:53
bike:7
barbie's dream house:5
guitar:6
colorful pencils:2
socks:1
shawl:2
karaoke machine:13
superman action doll set:3
guinea pig:3
horse:10
unicorn:8
sand toys:4
soccer shoes:3