簡體   English   中英

Haskell:如何在主要功能中使用HashMap

[英]Haskell: How to use a HashMap in a main function

懇請您的幫助,加快以下程序的速度:

main = do
  jobsToProcess <- fmap read getLine
  forM_ [1..jobsToProcess] $ \_ -> do
    [r, k] <- fmap (map read . words) getLine :: IO [Int]
    putStrLn $ doSomeReallyLongWorkingJob r k

可能有很多相同的工作要做,但是這不是我修改輸入的決定,因此我嘗試使用Data.HashMap備份已處理的工作。 我已經優化了doSomeReallyLongWorkingJob函數中的算法,但是現在看來,它和C一樣快。

但不幸的是,我似乎無法實現簡單的緩存而不會產生很多錯誤。 我需要一個簡單的類型為HashMap (Int, Int) Int緩存,但是每次括號太多或太少時。 而且,如果我設法定義緩存,那么我就會陷入將數據放入緩存或從緩存中檢索數據的問題,這會導致很多錯誤。

我已經在Google上搜索了幾個小時,但似乎被卡住了。 順便說一句: longrunner的結果也是一個Int

進行緩存操作的有狀態操作非常簡單。 首先一些樣板:

{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.State
import Data.Map (Map)
import qualified Data.Map as M
import Debug.Trace

我將使用Data.Map ,但是您當然可以在哈希映射或任何類似的數據結構中替換,而不會帶來太多麻煩。 我長時間運行的計算只會將其參數加起來。 我將使用trace來顯示何時執行此計算。 輸入重復的輸入后,我們希望不要看到trace的輸出。

reallyLongRunningComputation :: [Int] -> Int
reallyLongRunningComputation args = traceShow args $ sum args

現在,緩存操作將只是查詢我們之前是否已經看到過給定的輸入。 如果有,我們將返回預先計算出的答案; 否則,我們現在將計算出答案並將其存儲。

cache :: (MonadState (Map a b) m, Ord a) => (a -> b) -> a -> m b
cache f x = do
    mCached <- gets (M.lookup x)
    case mCached of
        -- depending on your goals, you may wish to force `result` here
        Nothing -> modify (M.insert x result) >> return result
        Just cached -> return cached
    where
    result = f x

現在, main功能只是在適當的輸入上調用cache reallyLongRunningComputation

main = do
    iterations <- readLn
    flip evalStateT M.empty . replicateM_ iterations
        $   liftIO getLine
        >>= liftIO . mapM readIO . words
        >>= cache reallyLongRunningComputation
        >>= liftIO . print

讓我們在ghci中嘗試一下!

> main
5
1 2 3
[1,2,3]
6
4 5
[4,5]
9
1 2
[1,2]
3
1 2
3
1 2 3
6

從括號中的輸出可以看到,當我們第一次輸入1 2 3和第一次輸入1 2 ,調用reallyLongRunningComputation ,但是第二次輸入這些輸入時未調用。

我希望我離基地不遠,但是首先,您需要一種與過去的工作一起工作的方法。 最簡單的方法是使用foldM而不是forM。

import Control.Monad
import Data.Maybe

main = do
  jobsToProcess <- fmap read getLine
  foldM doJobAcc acc0 [1..jobsToProcess] 
  where
    acc0 = --initial value of some type of accumulator, i.e. hash map
    doJobAcc acc _ = do
      [r, k] <- fmap (map read . words) getLine :: IO [Int]
      case getFromHash acc (r,k) of
         Nothing -> do 
           i <-  doSomeReallyLongWorkingJob r k
           return $ insertNew acc (r,k) i
         Just i -> do
            return acc

注意,我實際上並不使用該接口來放置和獲取哈希表鍵。 它實際上不必是哈希表,容器中的Data.Map可以工作。 甚至是一份清單(如果清單很小)。

攜帶哈希表的另一種方法是使用State轉換器monad。

我只是添加此答案,因為我覺得其他答案與原始問題有些出入,即在Main函數(在IO monad內部)中使用哈希表構造。

這是使用hashtables模塊的最小哈希表示例。 要使用cabal安裝模塊,只需使用

陰謀安裝哈希表

在此示例中,我們僅將一些值放入哈希表中,並使用查找打印從表中檢索到的值。

import qualified Data.HashTable.IO as H

main :: IO ()
main = do
        t <- H.new :: IO (H.CuckooHashTable Int String)
        H.insert t 22 "Hello world"
        H.insert t 5 "No problem"
        msg <- H.lookup t 5
        print msg

注意,我們需要使用顯式類型注釋來指定我們希望使用的哈希表實現。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM