使用管道4.0折叠流的子集

时间:2013-09-24 05:20:35

标签: haskell conduit haskell-pipes

我正在尝试理解管道4.0,并希望转换一些管道代码。假设我有一个Int的流,我想跳过前五个,然后得到以下的总和5.使用普通列表,这将是:

sum . take 5 . drop 5

在管道中,这将是:

drop 5
isolate 5 =$ fold (+) 0

或者作为一个完整的程序:

import Data.Conduit
import Data.Conduit.List (drop, isolate, fold)
import Prelude hiding (drop)

main :: IO ()
main = do
    res <- mapM_ yield [1..20] $$ do
        drop 5
        isolate 5 =$ fold (+) 0
    print res

但是,我不太确定如何用管道来做这件事。

2 个答案:

答案 0 :(得分:5)

我之前没有使用Pipes,但在完成教程后我发现它非常简单:

import Pipes
import qualified Pipes.Prelude as P

nums :: Producer Int IO ()
nums = each [1..20]

process :: Producer Int IO ()
process = nums >-> (P.drop 5) >-> (P.take 5)

result :: IO Int
result = P.fold (+) 0 id process

main = result >>= print

<强>更新

由于示例中没有“有效”处理,我们甚至可以使用Identity monad作为管道的基本monad:

import Pipes
import qualified Pipes.Prelude as P
import Control.Monad.Identity

nums :: Producer Int Identity ()
nums = each [1..20]

process :: Producer Int Identity ()
process = nums >-> (P.drop 5) >-> (P.take 5)

result :: Identity Int
result = P.fold (+) 0 id process

main = print $ runIdentity result

更新1

以下是我提出的解决方案(关于gist链接评论),但我觉得它可以更优雅

fun :: Pipe Int (Int, Int) Identity ()
fun = do
  replicateM_ 5 await
  a <- replicateM 5 await
  replicateM_ 5 await
  b <- replicateM 5 await
  yield (sum a, sum b)

main = f $ runIdentity $ P.head $ nums >-> fun where
  f (Just (a,b)) = print (a,b)
  f Nothing = print "Not enough data"

答案 1 :(得分:4)

要回答您的评论,这仍然适用于一般情况。我也在reddit上发布了相同的答案,你也在那里问过类似的问题,但我在这里重复答案:

import Pipes
import Pipes.Parse
import qualified Pipes.Prelude as P

main :: IO ()
main = do
    res <- (`evalStateT` (each [1..20])) $ do
        runEffect $ for (input >-> P.take 5) discard
        P.sum (input >-> P.take 5)
    print res

这将概括为您考虑的更复杂的案例。