2017-01-27 7 views
0

は、私は、あなたの助けを請う次のプログラムのスピードアップのメイン機能でHashMapを使用する方法ハスケル:(!):

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のシンプルなキャッシュが必要ですが、たいていは大括弧が少なすぎるか少なすぎます。そして、私がキャッシュを定義することができれば、私は多くのエラーのキャッシュにデータを入れたり、キャッシュからデータを取得したりしています。

私はすでに数時間グーグルでいたが、私は立ち往生しているようだ。 BTW:longrunnerの結果はIntです。

+0

落ち着いてください...まず問題を説明してください。どうやらあなたはジョブを処理したいと思っていますが、何らかの一意性フィルタが必要です。右? –

+0

いいえ、すべての入力に対して回答を書く必要があるため、一意性フィルタは必要ありません。 10ジョブ、10回答。同じ順序で。私は単にキャッシュが必要です。 – Hennes

+0

ああ話すのはどうですか? –

答えて

4

操作をキャッシュするステートフルなアクションを作成するのはかなり簡単です。まず、いくつかの決まり文句:

{-# 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を呼んで構成されています。

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 

あなたは括弧出力によって見ることができるように、reallyLongRunningComputationは、我々は1 2 3に入って初めて、我々は、これらの入力に入った二度目の1 2に入ったが、初めてではないと呼ばれていました。

+0

この素晴らしい例のおかげで多くの! – Hennes

1

私はあまり遠く離れていないと思っていますが、まずは過去の仕事を持ち歩く方法が必要です。最も簡単なのはforMの代わりにfoldMを使うことでしょう。

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が機能します。またはそれが小さいものになるならば、リストさえある。

ハッシュテーブルを持ち歩くもう1つの方法は、状態トランスモナドを使用することです。

+0

私はそれぞれの "返品"の前に直接私のputStrLnを挿入しますか? – Hennes

+0

私の恋愛のためだけに:State Monadで同じことをする方法を教えてもらえますか? – Hennes

+0

はいとはい。私はおそらくこの週末を更新することはできません。 –

0

他の答えが元の質問から少し分かれている、つまりメイン関数(IOモナド内)でハッシュテーブル構造を使用しているように感じるので、私はこの答えを追加しています。

hashtablesモジュールを使用する最小ハッシュテーブルの例です。徒党でモジュールをインストールするには、単に我々は単にハッシュテーブルにいくつかの値を入れて、テーブルから取得した値を印刷するために、ルックアップを使用し、

秘密結社は、ハッシュテーブルこの例で

をインストールしてください。

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 

明示的な型の注釈を使用して、使用したいハッシュテーブルの実装を指定する必要があることに注意してください。