Skip to content

Instantly share code, notes, and snippets.

@devshorts
Last active December 29, 2015 14:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devshorts/7686263 to your computer and use it in GitHub Desktop.
Save devshorts/7686263 to your computer and use it in GitHub Desktop.
shopping cart question
open System
type Percent = double
type Money = double
type Nth = int
type ProductType =
| Apple
| Orange
| Poo
type Coupon =
| NextItem of Percent
| AllItems of Percent
| Off of Nth * ProductType * Money
type Product = { Type: ProductType; Price: Money }
type Cart =
| Product of Product
| Coupon of Coupon
let cart = [ Coupon(Off (2, Apple, 0.5));
Product({ Type = Orange; Price = 1.0 });
Product({ Type = Poo; Price = 1.0 });
Coupon(AllItems(0.5));
Product({ Type = Apple; Price = 1.0 });
Product({ Type = Apple; Price = 1.0 });
Coupon(NextItem(0.5));
Product({ Type = Apple; Price = 1.0 });
Coupon(NextItem(0.90));
Product({ Type = Poo; Price = 1.0 });]
let selectProducts cart = cart |> Seq.choose (function | Product(p) -> Some(p) | _ -> None)
let updateCartElement f = function
| Coupon(c) -> Coupon(c)
| Product(p) -> Product(f p)
let ``by%`` percent cartItem =
cartItem |> updateCartElement (fun product -> { product with Price = product.Price * (1.0 - percent) })
let ``by$`` dollars cartItem =
cartItem |> updateCartElement (fun product -> { product with Price = product.Price - dollars})
let sumCart cart = Seq.fold (fun acc i -> i.Price + acc) 0.0 cart
let nthBy predicate (nth:int) (arr:'a[]) offset =
if nth = 0 then None else
let mutable n = nth
let mutable count = offset
while n <> 0 && count < (Array.length arr) - 1 do
count <- count + 1
if arr.[count] |> predicate then
n <- n - 1
if n = 0 then Some(count)
else None
let isProduct = function | Product(_) -> true | _ -> false
let isProductType productType = function | Product(p) when p.Type = productType -> true | _ -> false
let update elem (arr:'a []) predicate =
arr.[elem] <- predicate arr.[elem]
arr
let applyToAllItems percent cart = cart |> Array.map (``by%`` percent)
let updateIndx predicate cart idx =
match idx with
| Some(value) -> cart |> update value <| predicate
| None -> cart
let discountNextBy percent cart (offset:int) =
let nextIndx = cart |> nthBy isProduct 1 <| offset
nextIndx |> updateIndx (``by%`` percent) cart
let discountNthType (nth, productType, dollars) cart (offset:int) =
let nthIndex = cart |> nthBy (isProductType productType) nth <| offset
nthIndex |> updateIndx (``by$`` dollars) cart
let byCartElement (cart:Cart []) idx =
match cart.[idx] with
| Coupon(c) ->
match c with
| AllItems percent -> cart |> applyToAllItems percent
| NextItem percent -> (cart, idx) ||> discountNextBy percent
| Off (nth, productType, dollars) -> (cart, idx) ||> discountNthType (nth, productType, dollars)
| _ -> cart
let modifyList predicate arr =
arr
|> Array.fold(fun (count, arr) _ -> (count + 1, predicate arr count)) (0, arr)
|> snd
let applyCoupons (cart: Cart[]) = cart |> Array.copy |> modifyList byCartElement
let price (cart: Cart[]) = cart |> applyCoupons |> selectProducts |> sumCart
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment