(* * Homework number 5, solution * by Marek Kubica *) (* the type of products that we track *) type waren_typ = Buch | CD (* polymorphic product type to work on both integers and floats *) type 'a ware = { typ: waren_typ; name: string; preis: 'a } (* polymorphic client type to take any kind of product into the * shopping cart *) type 'a kunde = { kundenname: string; warenkorb: ('a ware * int) list } (* 5.3 a) *) (* adds any kind of product to the cart, while making sure there are * no duplicate entries. * The pattern matching buys us an incredible elegant option to acomplish * the check for uniqueness *) let rec add_to_cart a w c = match c with | [] -> if a > 0 then [(w, a)] else [] | (w2, n)::xs when w2 = w -> (w, n+a)::xs | x::xs -> x::add_to_cart a w xs (* 5.3 b) *) (* unify two carts into one *) let rec union_carts c1 c2 = match c1 with | [] -> c2 | (w, n)::xs -> union_carts xs (add_to_cart n w c2) (* 5.3 c) *) (* discounts all items of a certain type and name by d percent * This function is parametrized on float values, since using * percentages on integer values does not make much sense. * So there we have a split, between incompatible parametrized * types. We could probably use the object system to unify * the types, but the solution wouldn't be that functional anymore * * What it does: it unpacks the ware record if it is affected * and reinserts a new, discounted record at that place in the * list. Recursively. *) let rec discount typ name d c = match c with | [] -> [] | ({typ=typ_rec; name=name_rec; preis=preis_rec}, n)::xs when typ_rec = typ && name_rec = name -> ({typ=typ_rec; name=name_rec; preis=(1. -. float_of_int d /. 100.) *. preis_rec}, n)::discount typ name d xs | x::xs -> x::discount typ name d xs (* get the total cost of an item in the cart. * Unfortunately, this is hard-coded to integers, since floats * need *. and finding a generic solution is probably way out of * the scope of this homework *) let total_cost (w, n) = w.preis * n (* like OCaml's max, returns the larger of the two items *) let max_of_prices x y = if total_cost x >= total_cost y then x else y (* 5.3 d) max functions. A normal recursive one and a tail recursive *) (* We're using option types here, since empty lists have to have some * kind of return value. *) let rec maxlist = function | [] -> None | x::xs -> match (maxlist xs) with | None -> Some x | Some i -> Some (max_of_prices x i) let maxlist_tail = function | [] -> None | x::xs -> Some (List.fold_left max_of_prices x xs) (* some demo data *) (* a shopping cart, just like the example from the PDF. * This cart gets parametrized to the integer-type *) let cart = [({typ=Buch; name="S.King,Es"; preis=13}, 1); ({typ=CD; name="Beatles,1"; preis=15}, 3)] (* a float-parametrized list *) let fcart = [({typ=Buch; name="S.King,Es"; preis=13.}, 1); ({typ=CD; name="Beatles,1"; preis=15.}, 3); ({typ=CD; name="Beatles,1"; preis=20.}, 5)] (* final statement, now the expressions, therefore a ;; *) ;; (* add some books to the empty cart *) add_to_cart 5 {typ=Buch; name="S.King,Es"; preis=13} [];; (* adding some duplicates to the cart *) add_to_cart 5 {typ=Buch; name="S.King,Es"; preis=13} cart;; (* add some new books to the filled cart *) add_to_cart 5 {typ=Buch; name="S.King,Sie"; preis=13} cart;; (* add zero books to the cart *) add_to_cart 0 {typ=Buch; name="S.King,Sie"; preis=13} cart;; (* should double the cart contents *) union_carts cart cart;; (* discount the "Beatles,1" CD by 5% *) discount CD "Beatles,1" 5 fcart;; (* get the maximum of the cart *) maxlist cart;; (* use another strategy, but should yield an equivalent result *) maxlist_tail cart;;