1

我正在尝试使用pipes.

目前我有这个。但是不知道如何编写一个维护状态的管道。

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。

4

1 回答 1

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

此外,您可以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

一旦它是这种形式,我们就会看到我们没有使用管道的底层状态,除了 in 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
于 2016-02-22T03:34:25.673 回答