我是一名Haskell初学者,试图将我的脑袋缠绕在导管库上。
我尝试了类似的东西,但它没有编译:
import Data.Conduit
import Data.Conduit.Binary as CB
import Data.ByteString.Char8 as BS
numberLine :: Monad m => Conduit BS.ByteString m BS.ByteString
numberLine = conduitState 0 push close
where
push lno input = return $ StateProducing (lno + 1) [BS.pack (show lno ++ BS.unpack input)]
close state = return state
main = do
runResourceT $ CB.sourceFile "wp.txt" $= CB.lines $= numberLine $$ CB.sinkFile "test.txt"
似乎conduitState中的状态必须与管道的输入类型相同。或者至少这是我从错误消息中理解的:
$ ghc --make exp.hs
[1 of 1] Compiling Main ( exp.hs, exp.o )
exp.hs:8:27:
Could not deduce (Num [ByteString]) arising from the literal `0'
from the context (Monad m)
bound by the type signature for
numberLine :: Monad m => Conduit ByteString m ByteString
at exp.hs:(8,1)-(11,30)
Possible fix:
add (Num [ByteString]) to the context of
the type signature for
numberLine :: Monad m => Conduit ByteString m ByteString
or add an instance declaration for (Num [ByteString])
In the first argument of `conduitState', namely `0'
In the expression: conduitState 0 push close
In an equation for `numberLine':
numberLine
= conduitState 0 push close
where
push lno input
= return
$ StateProducing (lno + 1) [pack (show lno ++ unpack input)]
close state = return state
如何使用导管完成此操作?我想从文件中读取行并在每行附加行号。
答案 0 :(得分:6)
是的,可以做到。我更喜欢在Data.Conduit.List
中使用辅助函数,如果可能的话也避免使用Data.ByteString.Char8
。我假设您的文件是UTF-8编码。
import Data.Conduit
import Data.Conduit.Binary as CB
import Data.Conduit.List as Cl
import Data.Conduit.Text as Ct
import Data.Monoid ((<>))
import Data.Text as T
numberLine :: Monad m => Conduit Text m Text
numberLine = Cl.concatMapAccum step 0 where
format input lno = T.pack (show lno) <> T.pack " " <> input <> T.pack "\n"
step input lno = (lno+1, [format input lno])
main :: IO ()
main =
runResourceT
$ CB.sourceFile "wp.txt"
$$ Ct.decode Ct.utf8
=$ Ct.lines
=$ numberLine
=$ Ct.encode Ct.utf8
=$ CB.sinkFile "test.txt"
答案 1 :(得分:2)
close state = return state
这就是类型错误。您的close
函数的类型应为(state -> m [output])
(根据the docs)。在您的情况下state = Int
(您可能希望添加类型注释以确保它选择Int
)和output = BS.ByteString
,因此可能只返回空列表,因为在关闭管道时,你还没有真正保存任何ByteString
来制作或类似的东西。
close _ = return []
特别注意该论点的文档:
不需要返回状态,因为它不会再次使用
答案 2 :(得分:0)
使用pipes 3.0的替代解决方案,但它确实使用字符串而不是ByteString。我脑海中的主要优点是能够使用普通状态monad方法获取和放置。另一个好处是起始行号不会隐藏在addLineNumber(numberLine)中,因此可以更容易地从任何给定的行号开始。
import System.IO
import Data.Monoid ((<>))
import Control.Proxy
import qualified Control.Proxy.Trans.State as S
addLineNumber r = forever $ do
n <- S.get
line <- request r -- request line from file
respond $ show n <> " " <> line
S.put (n + 1) -- increments line counter
main =
withFile "wp.txt" ReadMode $ \fin ->
withFile "test.txt" WriteMode $ \fout ->
runProxy $ S.execStateK 1 -- start at line number at 1
$ hGetLineS fin >-> addLineNumber >-> hPutStrLnD fout
了解如何在宣布blog post of pipes-safe.时进行更细粒度的资源管理。