2012-06-03 26 views
5

Tôi có biến đơn nguyên tiếp theo:đầu ra Lazy từ hành động monadic

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

Về cơ bản, nó sử dụng cơ bản Iteratee mà đọc và xử lý tài liệu pdf (yêu cầu nguồn truy cập ngẫu nhiên, do đó nó sẽ không giữ cho tài liệu bộ nhớ mọi lúc).

Tôi cần triển khai một chức năng sẽ lưu tài liệu pdf và tôi muốn nó trở nên lười biếng, bạn có thể lưu tài liệu vào bộ nhớ không đổi.

tôi có thể sản xuất lười biếng 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 

Nhưng thực tế sản lượng có thể phụ thuộc vào sản lượng trước đó. (Chi tiết: tài liệu pdf có chứa cái gọi là "bảng tham chiếu" với độ lệch tuyệt đối theo byte của mỗi đối tượng bên trong tài liệu. Nó chắc chắn phụ thuộc vào độ dài của ByteString đối tượng pdf được tuần tự hóa.)

Làm thế nào để đảm bảo rằng chức năng save sẽ không buộc toàn bộ ByteString trước khi trả lại cho người gọi?

Có tốt hơn khi gọi lại làm đối số và gọi nó mỗi lần tôi có thứ gì đó để xuất?

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

Có giải pháp nào tốt hơn không?

Trả lời

0

Các giải pháp tôi đã tìm thấy cho đến nay là Coroutine Ví dụ:

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 

Nó làm công việc giống như gọi lại, nhưng người gọi có quyền kiểm soát đầy đủ về thế hệ đầu ra.

0

Để xây dựng điều này trong một vượt qua, bạn sẽ cần phải lưu trữ (có lẽ trong tiểu bang), nơi các đối tượng gián tiếp của bạn đã được viết. Vì vậy, tiết kiệm cần phải theo dõi vị trí byte tuyệt đối khi nó hoạt động - Tôi đã không xem xét liệu đơn nguyên Pdf của bạn có phù hợp với nhiệm vụ này hay không. Khi bạn kết thúc, bạn có thể sử dụng các địa chỉ được lưu trữ trong tiểu bang để tạo phần xref.

Tôi không nghĩ thuật toán hai bước sẽ giúp ích.

Chỉnh sửa ngày 6 tháng 6: Có lẽ tôi hiểu mong muốn của bạn tốt hơn bây giờ. Để tạo tài liệu rất nhanh, ví dụ: HTML, có một số thư viện trên hackage với "blaze" trong tên. Kỹ thuật này là để tránh sử dụng 'mconcat' trên ByteString và sử dụng ở trên loại trình tạo 'trung gian'. Thư viện cốt lõi cho điều này có vẻ là 'blaze-builder', được sử dụng trong 'blaze-html' và 'blaze-textual'.

+0

Tôi vừa thêm "triển khai" cho chức năng 'lưu' để giải quyết vấn đề. Có, nó phải là thuật toán 1-pass, nhưng nó không phải là một vấn đề. Bản thân vấn đề: khi tôi gọi 'mconcat' để tạo ra' ByteString' cuối cùng, tôi đã có nó trong bộ nhớ. Giả sử tập tin pdf rất lớn, tôi không có đủ bộ nhớ cho nó. Tôi chỉ muốn lưu trữ bù trừ, không phải chính bản thân 'ByteString'. Có vẻ như cách tiếp cận gọi lại giải quyết vấn đề, nhưng tôi nghĩ giải pháp tốt hơn nên tồn tại. – Yuras

+0

Lạ lùng, nhưng tôi không nhận được thông báo về chỉnh sửa của bạn vào ngày 6 tháng 6. 'Bulder' chắc chắn nhanh hơn' ByteString' khi bạn muốn 'mappend', nhưng vấn đề là sử dụng bộ nhớ, không phải hiệu năng. 'str1',' str2', vv sẽ có trong bộ nhớ (bắt buộc bởi BS.length) trước 'mconcat'. – Yuras

Các vấn đề liên quan