我尝试使用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.
答案 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和返回类型中达成一致。
以下是一些可能为您节省时间的进一步评论。首先produceFromList
是each
。
此外,您可以通过重新标记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([])