Letโs do a more direct translation of the Python code. You use coroutines in Python, so why not just use coroutines in Haskell? Then the question arises of mutable vectors; see below for more details.
First of all, tons of imports:
-- Import some coroutines import Control.Monad.Coroutine -- from package monad-coroutine -- We want to support "yield" functionality like in Python, so import it: import Control.Monad.Coroutine.SuspensionFunctors (Yield(..), yield) -- Use the lazy version of ST for statefulness import Control.Monad.ST.Lazy -- Monad utilities import Control.Monad import Control.Monad.Trans.Class (lift) -- Immutable and mutable vectors import Data.Vector (Vector) import qualified Data.Vector as Vector import Data.Vector.Mutable (STVector) import qualified Data.Vector.Mutable as Vector
Here are some utility definitions that allow coroutines to be processed as if they behaved as in Python, more or less:
Now we need to use the STVector
. You stated that you want to use a lazy ST, and the predefined operations on the STVector
defined only for strict ST, so we need to make several STVector
functions.I donโt do statements for such things, but you could if you really want to make pythonic code ( Eg $=
for writeLazy
or something else, you need to somehow handle the index projection, but it's possible that it looks the best anyway).
writeLazy :: STVector sa -> Int -> a -> ST s () writeLazy vec idx val = strictToLazyST $ Vector.write vec idx val readLazy :: STVector sa -> Int -> ST sa readLazy vec idx = strictToLazyST $ Vector.read vec idx thawLazy :: Vector a -> ST s (STVector sa) thawLazy = strictToLazyST . Vector.thaw
All the tools are here, so let's just translate the algorithm:
solveLP :: STVector s Int -> [[Bool]] -> Generator (ST s) [Int] solveLP vmax0 elems = go vmax0 elemTrueIxs where elemTrueIxs = [[ei | (ei, True) <- zip [0 :: Int ..] row] | row <- elems] go :: STVector s Int -> [[Int]] -> Generator (ST s) [Int] go _ [] = yield [] go vmax (m : ms) = do forM_ m $ \ ei -> do maxcnt <- lift $ readLazy vmax ei when (maxcnt > 0) $ do lift $ writeLazy vmax ei $ maxcnt - 1 sublist <- lift . generateList $ go vmax ms forM_ sublist $ \ es -> yield $ ei : es lift $ writeLazy vmax ei maxcnt
Unfortunately, no one bothered to define MonadPlus
for Coroutine
s, so guard
is not available here. But this is probably not what you wanted, as it causes an error when stopping in some / most monads. Of course, we also need to lift
all operations performed in the ST
monad from the Coroutine
monad; minor nuisance.
That is all the code, so you can just run it:
main :: IO () main = forM_ list print where list = runST $ do vmax <- thawLazy . Vector.fromList $ [1, 2, 3] generateList (solveLP vmax [[True, True, False], [True, False, True]])
The list
variable is clean and lazily generated.
I'm a little tired, so if something does not make sense, please feel free to point it out.