Haskell enumerator:类似于迭代器的“enumWith”运算符吗?

6
今天早些时候,我为迭代器编写了一个小测试应用程序,它组合了一个用于编写进度的迭代器和一个用于复制数据的迭代器。我最终得到了这样的值:
-- NOTE: this snippet is with iteratees-0.8.5.0
-- side effect: display progress on stdout
displayProgress :: Iteratee ByteString IO ()

-- side effect: copy the bytestrings of Iteratee to Handle
fileSink :: Handle -> Iteratee ByteString IO ()

writeAndDisplayProgress :: Handle -> Iteratee ByteString IO ()
writeAndDisplayProgress handle = sequence_ [fileSink handle, displayProgress]

在查看枚举器库时,我没有发现sequence_enumWith的类似物。我想做的就是将两个迭代器组合起来,使它们像一个迭代器一样工作。我可以放弃结果(它将是()),也可以保留它,我不在乎。我想要的是来自Control.Arrow的(&&&),只是针对迭代器而不是箭头。

我尝试了这两个选项:

-- NOTE: this snippet is with enumerator-0.4.10
run_ $ enumFile source $$ sequence_ [iterHandle handle, displayProgress]
run_ $ enumFile source $$ sequence_ [displayProgress, iterHandle handle]

第一个函数复制文件,但不显示进度;第二个函数显示进度,但不复制文件。显然,sequence_ 函数对枚举器的迭代器的影响是运行第一个迭代器直到其终止,然后运行另一个迭代器,这不是我想要的。我希望并行运行迭代器而不是串行运行。我感觉自己漏掉了一些明显的东西,但在阅读枚举器库中的 wc 示例时,我看到了这个奇怪的注释:
-- Exactly matching wc's output is too annoying, so this example
-- will just print one line per file, and support counting at most
-- one statistic per run

我想知道这句话是否意味着在枚举框架内组合或组成迭代器不可能。有没有通常被接受的正确方法来解决这个问题?
编辑:似乎没有内置的方法来完成这个任务。Haskell邮件列表中有关于添加组合器(如enumSequencemanyToOne)的讨论,但目前为止,在enumerator软件包中似乎没有提供此功能的任何东西。

我没有花足够长的时间查看文档来制定和测试一个真正的答案,但表面上看起来 EnumeratorEnumeratee 可以以你想要的方式组合。 (附言:我猜你在谈论 Hackage 上的“enumerator”包。你应该在一开始就说出这样的话,因为现在有大约半打不同的迭代器实现。=) - Daniel Wagner
你也没有花足够的时间阅读我的问题。 - Daniel Lyons
这有点不礼貌,考虑到我刚刚花了一些“我的”时间来帮助你解决“你的”问题。与其嘲讽,也许你可以指出你认为我错过的部分。 - Daniel Wagner
如果您查看我的代码注释,您会发现我清楚地说明了代码片段适用的哪个库的哪个版本,并且您还会看到我已经明确指出我正在尝试组合的是iteratees,而不是enumeratees或enumerators,而文档确实展示了如何组合它们。我并不想无礼,但如果您想提供帮助,您应该阅读整个问题并理解它,而不仅仅告诉我去读手册,这显然我已经做过了。如果答案如此明显,为什么需要三天时间才有人说“它在手册中”? - Daniel Lyons
哈哈!你关于库版本的说法绝对正确。我很抱歉——我的眼睛经常忽略注释,这次我真的受到了打击。然而,我不认为你明确知道可以组合枚举器;事实上,最后两句话,“我想知道……组合迭代器……是否可以直接使用。什么是通常被接受的正确方法?”可以有意义地回答“通常被接受的正确方法是通过组合枚举器而不是迭代器来实现”。很抱歉这对你没有帮助。 - Daniel Wagner
1个回答

2

我认为,与其尝试让两个Iteratees并行消耗序列,更好的方法是将流通过一个身份Enumeratee,简单地计算通过它的字节数。

下面是一个简单的示例,它复制一个文件,并在每个块后打印复制的字节数。

import System.Environment
import System.IO
import Data.Enumerator
import Data.Enumerator.Binary (enumFile, iterHandle)
import Data.Enumerator.List (mapAccumM)
import qualified Data.ByteString as B

printBytes :: Enumeratee B.ByteString B.ByteString IO ()
printBytes = flip mapAccumM 0 $ \total bytes -> do
    let total' = total + B.length bytes
    print total'
    return (total', bytes)

copyFile s t = withBinaryFile t WriteMode $ \h -> do
    run_ $ (enumFile s $= printBytes) $$ iterHandle h

main = do
    [source, target] <- getArgs
    copyFile source target

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接