2012-06-03 6 views
5

Ich habe den nächsten Monade Transformator:Faule Ausgabe von monadischen Aktion

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

Grundsätzlich nutzt zugrunde liegende Iteratee, der liest und verarbeitet PDF-Dokument (erfordert Random-Access-Quelle, so dass es das Dokument nicht hält in Speicher die ganze Zeit).

Ich muss eine Funktion implementieren, die PDF-Dokument speichern wird, und ich möchte, dass es faul ist, sollte es möglich sein, Dokument in konstantem Speicher zu speichern.

ich faul ByteString produzieren kann:

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 

Aber tatsächliche Leistung bei früheren Ausgabe verlassen können. (Details:.. PDF-Dokument so „Referenztabelle“ mit absoluter Offset in Bytes jedes Objekt innerhalb des Dokuments genannt enthält Es hängt definitiv von der Länge des ByteString pdf Objekts serialisiert)

Wie diese save Funktion, um sicherzustellen, wird nicht erzwinge ganz ByteString, bevor es zum Anrufer zurückbringt?

Ist es besser, Callback als Argument zu nehmen und es jedes Mal aufzurufen, wenn ich etwas ausgeben möchte?

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

Gibt es eine bessere Lösung?

Antwort

0

Die Lösung, die ich bisher gefunden ist Coroutine Beispiel:

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 

Es tut die gleiche Arbeit wie Rückruf, aber Anrufer hat die volle Kontrolle über Ausgang Generation.

0

Um dies in einem Durchgang zu erstellen, müssen Sie (vielleicht in dem Zustand), in dem Ihre indirekten Objekte geschrieben wurden, speichern. Das Speichern muss also die absolute Byte-Position im Auge behalten, da es funktioniert - ich habe nicht berücksichtigt, ob Ihre Pdf-Monade für diese Aufgabe geeignet ist. Wenn Sie das Ende erreicht haben, können Sie die im Status gespeicherten Adressen verwenden, um den XRef-Abschnitt zu erstellen.

Ich glaube nicht, dass ein Zwei-Durchlauf-Algorithmus helfen wird.

Edit 6. Juni: Vielleicht verstehe ich Ihren Wunsch jetzt besser. Für eine sehr schnelle Erzeugung von Dokumenten, z.B. HTML, gibt es mehrere Bibliotheken auf hackage mit "Blaze" im Namen. Die Technik besteht darin, die Verwendung von 'mconcat' im ByteString zu vermeiden und in einem 'Builder'-Zwischenformat zu verwenden. Die Kernbibliothek dafür scheint 'blaze-builder' zu sein, die in 'Blaze-HTML' und 'Blaze-Text' verwendet wird.

+0

Ich habe gerade eine "Implementierung" für die 'save' Funktion hinzugefügt, um das Problem zu verdeutlichen. Ja, es sollte ein 1-Pass-Algorithmus sein, aber das ist kein Problem. Das Problem selbst: Wenn ich 'mconcat' anrufe, um den endgültigen faulen' ByteString' zu erzeugen, habe ich es bereits im Speicher. Unter der Annahme einer sehr großen PDF-Datei habe ich nicht genug Speicher dafür. Ich möchte nur Offsets speichern, nicht den ByteString selbst. Der Callback-Ansatz scheint das Problem zu lösen, aber ich denke, es sollte eine bessere Lösung geben. – Yuras

+0

Seltsam, aber ich habe keine Benachrichtigung über deine Bearbeitung am 6. Juni erhalten. Wie kann 'blaze-builder' mir helfen? 'Bulder' ist definitiv schneller als' ByteString', wenn Sie 'mappend' wollen, aber das Problem ist die Speichernutzung, nicht die Leistung. 'str1',' str2' usw. befinden sich bereits im Speicher (erzwungen von BS.length) vor 'mconcat'. – Yuras