5

我有下一个单子变压器:

newtype Pdf' m a = Pdf' {
  unPdf' :: StateT St (Iteratee ByteString m) a
  }
type Pdf m = ErrorT String (Pdf' m)

基本上,它使用 Iteratee读取和处理pdf文档的底层(需要随机访问源,这样它就不会一直将文档保存在内存中)。

我需要实现一个保存pdf文档的功能,我希望它是懒惰的,应该可以将文档保存在常量内存中。

我可以产生懒惰ByteString

import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as BS
save :: Monad m => Pdf m ByteString
save = do
  -- actually it is a loop
  str1 <- serializeTheFirstObject
  storeOffsetForTheFirstObject (BS.length str1)
  str2 <- serializeTheSecondObject
  storeOffsetForTheSecondObject (BS.length str2)
  ...
  strn <- serializeTheNthObject
  storeOffsetForTheNthObject (BS.length strn)
  table <- dumpRefTable
  return mconcat [str1, str2, ..., strn] `mappend` table

但实际输出可能取决于先前的输出。(详细信息:pdf 文档包含所谓的“参考表”,其中包含文档内每个对象的绝对偏移量(以字节为单位)。这绝对取决于ByteStringpdf 对象序列化到的长度。)

如何确保该函数在将其返回给调用者之前save不会强制执行?ByteString

将回调作为参数并在每次我有输出时调用它会更好吗?

import Data.ByteString (ByteString)
save :: Monad m => (ByteString -> Pdf m ()) -> Pdf m ()

有更好的解决方案吗?

4

2 回答 2

0

要一次性构建它,您需要存储(可能在状态中)已写入间接对象的位置。所以保存需要在工作时跟踪绝对字节位置——我没有考虑你的 Pdf monad 是否适合这个任务。当您完成时,您可以使用存储在状态中的地址来创建外部参照部分。

我认为两遍算法不会有帮助。

6 月 6 日编辑:也许我现在更了解你的愿望了。对于非常快速的文档生成,例如 HTML,有几个关于hackage的库,名称中带有“blaze”。该技术是避免在 ByteString 上使用“mconcat”并在中间“builder”类型上使用。核心库似乎是'blaze-builder',用于 'blaze-html' 和 'blaze-textual'。

于 2012-06-03T22:39:47.970 回答
0

到目前为止我找到的解决方案是协程 示例:

proc :: Int -> Coroutine (Yield String) IO ()
proc 0 = return ()
proc i = do
  suspend $ Yield "Hello World\n" (proc $ i - 1)

main :: IO ()
main = do
  go (proc 10)
  where
  go cr = do
    r <- resume cr
    case r of
      Right () -> return ()
      Left (Yield str cont) -> do
        putStr str
        go cont

它与回调的工作相同,但调用者可以完全控制输出生成。

于 2012-06-21T08:44:08.587 回答