Calculating the maximum profit from stocks: Purescript Leetcode Solution (Updated to use Typeclass)
druchan
Posted on July 18, 2023
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
Posted on July 18, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
July 18, 2023