保持状态的管道

时间:2016-02-22 01:59:11

标签: haskell haskell-pipes

我尝试使用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.

1 个答案:

答案 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 liftConsumer Word64 (StateT Word64 IO) ()系列a >-> b >-> c中的所有内容都必须在底层monad和返回类型中达成一致。

以下是一些可能为您节省时间的进一步评论。首先produceFromListeach

此外,您可以通过重新标记hoist lift来避免stdoutLn

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

然后你会写

Uncaught Error: Module name "core" has not been loaded yet for context: _. Use require([])