In the last note posted, we "implicitly" defined some valid trades.
Two data structures specifically had this notion associated with them:
-- type Day = Int -- type Price = Int type StockDay = Tuple Price Day -- and data BuySell = BuySell StockDay StockDay
Two StockDay
s could be valid only if they happen on subsequent days.
That is:
a = Tuple _ 1 b = Tuple _ 6 c = Tuple _ 8 d = Tuple _ 4
In this, a
followed by b
can be a valid trade because the "day" order makes sense. (That is a
is on 1st day and b
is on 6th day - buy on 1st and sell on 6th).
But c
followed by d
(and b
followed by d
) are not valid because the day orders are reversed.
This notion comes into the picture when we "pair" these to make a BuySell
combination.
We expressed this as a Maybe
:
makeBuySell :: StockDay -> StockDay -> Maybe BuySell makeBuySell t1@(Tuple p1 a) t2@(Tuple p2 b) = if a < b && p1 < p2 then Just (BuySell t1 t2) else Nothing
That a < b
part takes care of the "valid trade" logic.
It's OK and there's no need to "improve" this...
But now, there's also this other function that we use in our computation:
isValidTradeDayOrder :: BuySell -> BuySell -> Boolean isValidTradeDayOrder (BuySell (Tuple _ a) (Tuple _ b)) (BuySell (Tuple _ c) (Tuple _ d)) = (a < b && c < d && c > b) || (c < d && a < b && d < a)
Here, we're trying to confirm if two buy-sell pairs are actually valid by making sure the dates/days align. (Remember that within a BuySell
pair, the StockDay
s are valid thanks to our makeBuySell
function. Essentially, a BuySell
represents valid, profitable buy-and-sell operation).
Again, though, in the case of a isValidTradeDayOrder
, we're dealing with this idea of "valid trade".
I decided (or rather, wanted to experiment) if this general idea of a "valid trade" can be encoded into the code as a general function.
Purescript, like Haskell, allows you to define your own typeclasses and then define instances for your data types.
So, here's a generic ValidTrade
class:
class ValidTrade a where validTrade :: a -> a -> Boolean
Any datatype using the ValidTrade
class must simply describe a validTrade
function.
And so, here are the definitions for both the StockDay
and BuySell
data types:
instance ValidTrade BuySell where validTrade (BuySell (Tuple _ a) (Tuple _ b)) (BuySell (Tuple _ c) (Tuple _ d)) = (a < b && c < d && c > b) || (c < d && a < b && d < a) instance ValidTrade StockDay where validTrade (Tuple _ d1) (Tuple _ d2) = d1 < d2
I also added an infix
to help make the code a little succinct:
infix 1 validTrade as ??
Now, I could get rid of the isValidTradeDayOrder
function in totality and replace some instances with ??
:
makeBuySell :: StockDay -> StockDay -> Maybe BuySell makeBuySell t1@(Tuple p1 _) t2@(Tuple p2 _) = if (t1 ?? t2) && p1 < p2 then Just (BuySell t1 t2) else Nothing bestBuySellPair :: BuySell -> SortedArray (BuySell) -> Int -> Maybe BestCandidate -> Maybe BestCandidate bestBuySellPair buySell (SortedArray []) maxProfitSoFar bestTradeSoFar = if buySellProfit buySell > maxProfitSoFar then (Just $ Tuple buySell Nothing) else bestTradeSoFar bestBuySellPair buySell (SortedArray tradePairs) maxProfitSoFar bestTradeSoFar = case head tradePairs of Just h -> -- notice the ?? here. we've replaced the `isValidTradeDayOrder` function with ?? if ((buySell ?? h) && (buySellProfit buySell + buySellProfit h) > maxProfitSoFar) then bestBuySellPair buySell (fromMaybe (SortedArray []) (map SortedArray $ tail tradePairs)) (buySellProfit buySell + buySellProfit h) (Just $ Tuple buySell (Just h)) else bestBuySellPair buySell (fromMaybe (SortedArray []) (map SortedArray $ tail tradePairs)) maxProfitSoFar bestTradeSoFar Nothing -> if buySellProfit buySell > maxProfitSoFar then (Just $ Tuple buySell Nothing) else bestTradeSoFar
And the script continues to run just fine:
> totalProfits [3,1,0,0,5,4,1] 5 > totalProfits [3,1,0,0,1] 1 > totalProfits [3,1,0,0,1,2,3] 3
Top comments (0)