----------------------------------------------------------------------------- -- Copyright 2019, Advise-Me project team. This file is distributed under -- the terms of the Apache License 2.0. For more information, see the files -- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution. ----------------------------------------------------------------------------- -- | -- Maintainer : bastiaan.heeren@ou.nl -- Stability : provisional -- Portability : portable (depends on ghc) -- ----------------------------------------------------------------------------- module Util.Cache ( cached, cached2, cached3, cached4 , printCache, writeCache ) where import Control.Monad import qualified Data.Map as M import Data.IORef import System.IO.Unsafe cached :: (Ord a, Show a, Show b) => String -> (a -> b) -> a -> b cached n = withCache . newCache n cached2 :: (Ord a, Ord b, Show a, Show b, Show c) => String -> (a -> b -> c) -> a -> b -> c cached2 s f = curry $ cached s $ uncurry $ f cached3 :: (Ord a, Ord b, Ord c, Show a, Show b, Show c, Show d) => String -> (a -> b -> c -> d) -> a -> b -> c -> d cached3 s f = curry3 $ cached s $ uncurry3 $ f where curry3 f a b c = f (a, b, c) uncurry3 f (a, b, c) = f a b c cached4 :: (Ord a, Ord b, Ord c, Ord d, Show a, Show b, Show c, Show d, Show e) => String -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> e cached4 s f = curry4 $ cached s $ uncurry4 $ f where curry4 f a b c d = f (a, b, c, d) uncurry4 f (a, b, c, d) = f a b c d data Cache a b = Cache { name :: String , function :: a -> b , showKey :: a -> String , showValue :: b -> String , counter :: IORef Int , cache :: IORef (M.Map a b) } newCache :: (Show a, Show b) => String -> (a -> b) -> Cache a b newCache n f = unsafePerformIO $ do r <- newIORef M.empty cnt <- newIORef 0 let c = Cache n f show show cnt r modifyIORef register (c:) return c withCache :: Ord a => Cache a b -> a -> b withCache c a = unsafePerformIO $ do modifyIORef (counter c) (+1) m <- readIORef (cache c) case M.lookup a m of Just b -> return b Nothing -> do let b = function c a writeIORef (cache c) (M.insert a b m) return b ------------------------------------------------------------------------ register :: IORef [Cache a b] register = unsafePerformIO (newIORef []) printCache :: IO () printCache = showCache >>= putStrLn writeCache :: FilePath -> IO () writeCache file = showCache >>= writeFile file showCache :: IO String showCache = do xs <- readIORef register fmap unlines $ forM xs $ \c -> do n <- readIORef (counter c) m <- readIORef (cache c) let sz = M.size m line = replicate 50 '-' f (a, b) = showKey c a ++ ": " ++ showValue c b summary = name c ++ ": size=" ++ show sz ++ ", access=" ++ show n ++ ", efficiency=" ++ showRatio (n-sz) n return $ unlines $ summary : line : map f (M.toList m) showRatio :: Int -> Int -> String showRatio a b = show rd where r = (fromIntegral a / fromIntegral b) :: Double rn = round (r*1000) :: Int rd = (fromIntegral rn/1000) :: Double {- -- example: fib :: Int -> Int fib = cached "fib" $ \n -> if n == 0 then 0 else if n == 1 then 1 else fib (n-1) + fib (n-2) main :: IO () main = do print (fib 50) printCache -}