2016-02-22 335 views
1

我正在嘗試使用pipes來計算大文件的滾動哈希值(buzzhash)。維護狀態的管道

目前我有這個。但不知道如何編寫保持狀態的管道。

import qualified Data.ByteString.Lazy as L 
import Data.Word 
import Data.Bits(xor,rotate) 
import Data.Array 
import Pipes 
import Control.Monad.State.Strict 
import Control.Monad(forever) 

produceFromList (x:xs) = do 
    yield x 
    produceFromList xs 

buzzHash = do 
    x <- await 
    h <- lift $ get -- pull out previous value 
    let h' = rotate h 1 `xor` (hashArrW8!x) -- calculate new value 
    lift $ put h' -- save new value 
    yield h' 

stdoutLn :: Consumer Word64 IO() 
stdoutLn = do 
    a <- await 
    lift $ print a 

main = do 
    bs <- L.unpack `fmap` L.getContents 
    runEffect $ produceFromList bs >-> buzzHash >-> stdoutLn 

hashArrW8 :: Array Word8 Word64 

如何使buzzHash保存以前的值並將其用於計算下一個值?初始狀態值應爲0.

回答

3

您幾乎在那裏;你只需要運行狀態。

main = do 
    bs <- L.unpack `fmap` L.getContents 
    flip execStateT 0 $ runEffect $ produceList bs >-> buzzHash >-> hoist lift stdoutLn 

我假設你不想找回狀態,所以我用execStateT而非runStateT

這裏唯一的好奇是stdoutLn被標記爲Consumer Word64 IO()。因此,我使用hoist lift來製作它Consumer Word64 (StateT Word64 IO)()系列a >-> b >-> c中的所有內容都必須在基礎monad和返回類型中達成一致。

以下是一些可能爲您節省時間的進一步評論。第一個produceFromListeach

此外,你可能會被重新標記您的stdoutLn避免hoist lift

stdoutLn :: MonadIO m => Consumer Word64 m() 
stdoutLn = do 
    a <- await 
    liftIO $ print a 

但這裏有一些麻煩:你是不是重複的動作。這應該很清楚是一個循環:

stdoutLn :: MonadIO m => Consumer Word64 m() 
stdoutLn = do 
    a <- await 
    liftIO $ print a 
    stdoutLn 

其實這已經可以作爲P.print,所以我們可以寫

import qualified Pipes.Prelude as P 
main = do 
    bs <- L.unpack `fmap` L.getContents 
    flip execStateT 0 $ runEffect $ each bs >-> buzzHash >-> P.print 

如果我理解你,buzzHash意味着太無限期地重複:

buzzHash = do 
    x <- await 
    h <- lift $ get -- pull out previous value 
    let h' = rotate h 1 `xor` (hashArrW8!x) -- calculate new value 
    lift $ put h' -- save new value 
    yield h' 
    buzzHash 

(這是forever buzzHash,在這裏我們使用您的buzzHash

最後,如果你

import qualified Pipes.ByteString as PB 
import Control.Lens (view) -- (or Lens.Micro.MTL or Lens.Simple) 

我們看到我們不需要懶惰的字節串IO,不正確流反正。 Pipes.ByteString已經有我們想要的unpack,打包成鏡頭,以便我們在其他地方使用view PB.unpack我們將使用B.unpack。那麼到底我們可以寫

main = flip evalStateT 0 $ runEffect $ view PB.unpack PB.stdin >-> buzzHash >-> P.print 

一旦處於這種形式,我們看到我們沒有使用管道的基本狀態,除了在buzzHash,所以我們可以本地化此

import Pipes.Lift (evalStateP) 
main = runEffect $ view PB.unpack PB.stdin >-> evalStateP 0 buzzHash >-> P.print 

或如果你喜歡,你可以重寫

buzzHash' :: Monad m => Word64 -> Pipe Word8 Word64 m r 
buzzHash' n = evalStateP n $ forever $ do 
    x <- await 
    h <- lift $ get -- pull out previous value 
    let h' = rotate h 1 `xor` (hashArrW8!x) -- calculate new value 
    lift $ put h' -- save new value 
    yield h' 

然後你會寫

main = runEffect $ view PB.unpack PB.stdin >-> buzzHash' 0 >-> P.print 
+0

是在狀態monad中的'buzzHash'之後的所有內容? –

+1

通過'> - >'全部轉換'(StateT Word64 IO)'鏈接的三個管道事件,是的。因此,'buzzHash'專門用於'管道Word8 Word64(StateT Word64 IO)()','P.print'專門用於'客戶端Word64(StateT Word64 IO)()'和'查看PB.unpack PB.stdin'到'Producer Word8(StateT Word64 IO)()'來使用類型同義詞管道使用。所以當你將它們並排在一起時,你會得到一個'Effect(StateT Word64 IO)()'。那麼,當你應用'runEffect'時,你會得到一個'StateT Word64 IO()'。接下來,當你應用'runStateT'時你有一個'Word64 - > IO((),Word64)'。 – Michael

+1

我在最後加入了一些關於'evalStateP'等的內容。也許這更合適,因爲它不涉及調整管道,以便一切都一致。相反,我們只是編寫一個'buzzHash'函數,該類型中沒有明確涉及'StateT'。 – Michael