2013-04-09 15 views
5

içinde bazı girdi alır ve çıkış verecek IO hesaplama döndüren bir işlevMemoizing IO Hesaplamalar Haskell'e

f :: MonadIO m => a -> m b 

sahiptir. f'u "not almayı" istiyorum, böylece sadece bu hesaplamaları her giriş için bir kez gerçekleştireceğim. Örneğin,

f :: String -> IO String 
f s = putStrLn ("hello " ++ s) >> return s 

eğer o zaman bir fonksiyonu memoize böyle

o
do 
    mf <- memoize f 
    s <- mf "world" 
    t <- mf "world" 
    return (s,t) 

baskılar "hello world" yalnızca bir kez ve ("world", "world") döndürür istiyorum. Yazdığım program çok iş parçacıklı olduğundan, bu ileti farklı iş parçacıkları çağıran bile olsa beklemelidir mf.

Şimdiye kadar geldiğim (korkunç) çözüm. Sorum şu: ve nasıl geliştirilebileceği.

1) cacheTVar (Map a (TMVar (Maybe b))) yazın sahiptir:

memoize :: (MonadIO m, Ord a) => (a -> m b) -> m (a -> m b) 
memoize f = do 
    cache <- liftIO $ newTVarIO Map.empty 
    return $ \a -> do 
       v <- liftIO $ atomically $ lookupInsert cache a 
       b <- maybe (f a) return =<< liftIO (atomically $ takeTMVar v) 
       liftIO $ atomically $ putTMVar v $ Just b 
       return b 
    where 
     lookupInsert :: Ord a => TVar (Map a (TMVar (Maybe b))) -> a -> STM (TMVar (Maybe b)) 
     lookupInsert cache a = do 
         mv <- Map.lookup a <$> readTVar cache 
         case mv of 
          Just v -> return v 
          Nothing -> do 
            v <- newTMVar Nothing 
            modifyTVar cache (Map.insert a v) 
            return v 

birkaç şey burada devam bulunmaktadır. Girdi değerleri ya hesaplanmış değer içeren TMVar 'a ya da Nothing kodludur (bu, bir değerin henüz hesaplanmamış olduğunu gösterir). lookupInsert işlevi, cache denetimini gerçekleştirir ve zaten mevcut değilse Nothing numaralı yeni bir TMVar ekler.

2) geri hareket ilk a ile ilişkili, o zaman alır v :: TMVar (Maybe b) elde eder ve, ya mevcut ise Maybe depolanan değeri bir sonuç elde etmek için hesaplama f a yerine ya da döner. Bu take ve put desen, iki farklı iş parçacığının her ikisi de henüz çalıştırılmamış olduğunu gördükten sonra hesaplamayı f a çalıştırmaz.

+0

Haritadaki değerlerin ya TVM (Belki a) 'veya' TMVar b' olmasını istediğinizi (bence başlığın altındaki TVar (Belki b) 'ye eşdeğerdir). Sadece bir taneye ihtiyacın var gibi göründüğünde iki kat boşluk var. İkincisi, tüm "atomik" eylemlerinizi, yarış koşullarından kaçınmak için tek bir işlemde birleştirmelisiniz. –

+0

'Atomik' eylemleri nasıl birleştireceğinden emin değilim çünkü ortada hesaplamayı yapmak zorunda kalacağız. İkincisi monad 'm' içinde var, bu yüzden 'take' ve 'put'' m' için kaldırılması gerektiği gibi görünüyor. – davidsd

+0

Bana göre yanlış monad içinde olduğun gibi görünüyor. – leftaroundabout

cevap