में मैं एक समारोहMemoizing आईओ संगणना हास्केल
f :: MonadIO m => a -> m b
जो कुछ इनपुट लेता है और एक आईओ गणना कि उत्पादन निकलेगा देता है। मैं f
को "याद रखना" चाहता हूं ताकि मैं प्रत्येक इनपुट के लिए कभी-कभी इन गणनाओं को निष्पादित करूं। उदाहरण के लिए, यदि
f :: String -> IO String
f s = putStrLn ("hello " ++ s) >> return s
तो मैं एक समारोह चाहते हैं इस तरह के memoize
कि
do
mf <- memoize f
s <- mf "world"
t <- mf "world"
return (s,t)
प्रिंट "hello world"
ठीक एक बार और ("world", "world")
देता है। जो प्रोग्राम मैं लिख रहा हूं वह बहु-थ्रेडेड है, इसलिए इस प्रॉपर्टी को तब भी पकड़ना चाहिए जब विभिन्न धागे mf
पर कॉल कर रहे हों।
नीचे (भयानक) समाधान है जो मैं अब तक आया हूं। मेरा सवाल यह है कि यह कैसे और कैसे सुधार किया जा सकता है।
1) cache
टाइप TVar (Map a (TMVar (Maybe b)))
है:
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
कुछ चीजें यहाँ पर जा रहे हैं। यह TMVar
के इनपुट को मैप करता है जिसमें या तो गणना की गई मान, या Nothing
(जो इंगित करता है कि एक मान अभी तक गणना नहीं किया गया है)। फ़ंक्शन lookupInsert
cache
का निरीक्षण करता है, और TMVar
को Nothing
में प्रारंभ किया गया है यदि कोई भी पहले से मौजूद नहीं है।
2) लौटे कार्रवाई पहले प्राप्त v :: TMVar (Maybe b)
a
से जुड़े हैं, तो यह लेता है, और या तो गणना f a
करता है एक परिणाम प्राप्त करने के लिए या रिटर्न Maybe
में जमा हो जाती है, तो यह उपलब्ध है मूल्य। यह take
और put
पैटर्न इतना है कि दो अलग-अलग धागे गणना के बाद f a
गणना नहीं करते हैं, यह अभी तक नहीं चला है।
मुझे लगता है कि आप मानचित्र में मानों को 'टीवीर (शायद ए)' या 'टीएमवीर बी' (जो हुड के नीचे 'टीवीर (शायद बी) के बराबर है) चाहते हैं। आपके पास खालीपन की दो परतें होती हैं जब ऐसा लगता है कि आपको केवल एक की आवश्यकता है। दूसरा, आपको दौड़ की स्थिति से बचने के लिए अपने सभी 'परमाणु' कार्यों को एक ही लेनदेन में जोड़ना चाहिए। –
मुझे यकीन नहीं है कि 'परमाणु रूप से' कार्रवाइयों को कैसे जोड़ा जाए क्योंकि हमें बीच में गणना 'एफ ए' करना पड़ सकता है। उत्तरार्द्ध monad 'm' में मौजूद है, इसलिए ऐसा लगता है कि 'टेक' और' put' को 'm' पर उठाया जाना चाहिए। – davidsd
थोड़ा सा लगता है जैसे आप मेरे लिए गलत मोनड में हैं। – leftaroundabout