I have the following function in Haskell:
memdb = -- load the contents of a database into memory as a Map
And then I have the following line:
map (\x -> memdb ! x) values
I would like memdb to generate the Map only once, instead of on every iteration of map
. I could do it using something like this:
make_memdb = -- equivalent to memdb in previous example
memdb <- make_memdb
map (\x -> memdb ! x) values
But this would mean that I would have to pass memdb
in to every function that uses it. Is there any way I can:
a. avoid recalculating memdb
each time it is called OR
b. save the value produced in make_memdb
as a constant so I can avoid passing it in to every function that uses it?
In programming, memoization is an optimization technique that makes applications more efficient and hence faster. It does this by storing computation results in cache, and retrieving that same information from the cache the next time it's needed instead of computing it again.
To memoize a function in Python, we can use a utility supplied in Python's standard library—the functools. lru_cache decorator. Now, every time you run the decorated function, lru_cache will check for a cached result for the inputs provided. If the result is in the cache, lru_cache will return it.
The right way to do memoization in C++ is to mix the Y-combinator in. Your base function needs a modification. Instead of calling itself directly, it takes a templateized reference to itself as its first argument (or, a std::function<Same_Signature> recursion as its first argument). We start with a Y-combinator.
In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
As your map comes from a database, that means it cannot be a constant as it can be different between runs of your application.
But this would mean that I would have to pass memdb in to every function that uses it.
Yes, but there are tools to make this less bad than it sounds. In particular, this sounds like a perfect use case for the the reader monad!
Reader monads are commonly used when you have some value, like a configuration, that you want to load at the start of your program and then be able to access it around your program without having to explicitly pass it around all the time. Here's a short example of how you would use it:
main = do
memdb <- make_memdb -- Get the memdb from the database once and for all
runReaderT foo memdb
foo = do
memdb <- ask -- Grab the memdb. Will not reload from the database
liftIO $ putStrLn "Hello, world" -- IO actions have to be lifted
-- [...]
See also:
You seem to want to get the memdb
via IO
as a way of avoiding passing more parameters, correct? You then ask if you can either (A) define memdb
, implying it will be a top-level function, without the overhead of loading data from the DB or (B) if you can save the loaded data structure with global scope.
Both of these are doable with an IORef
and unsafePerformIO
to define a top-level mutable global variable. I DO NOT SUGGEST YOU DO THIS. It is clumsy and annoying to refactor. That said, I'll show you how anyway:
Assuming you have a function:
make_memdb :: IO (Map K V)
You can declare a top-level mutable variable:
import Data.Map as M
import Data.IORef
mRef :: IORef (Map K V)
mRef = unsafePerformIO $ newIORef M.empty
{-# NOINLINE mRef #-}
main = do
m <- make_memdb
writeIORef mRef m
... do stuff using mRef ...
stuffUsingMRef ... = do
memdb <- readIORef
let vs = map (memdb !) values
return vs
Notice that your functions will forever live in IO
. This is because you need IO
in order to read the global mutable variable in which you placed memdb
. If you don't like this, and you don't like passing parameters, then learn the state monad! I'm sure another answer will discuss that, which is the correct solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With