2011-06-01 10 views
18

में अपरिभाषित लंबाई की सूचियों के लिए बाइनरी सीरियलाइजेशन मैं डेटा का उपयोग कर रहा हूं। फाइलों को डेटा को क्रमबद्ध करने के लिए। मेरे आवेदन में मैं इन फ़ाइलों में वृद्धिशील रूप से आइटम जोड़ता हूं। दो सबसे लोकप्रिय धारावाहिक पैकेज, बाइनरी और अनाज, दोनों क्रमबद्ध सूची सूची के बाद क्रमबद्ध सूची के रूप में सूचीबद्ध हैं। इस वजह से, मैं अपनी धारावाहिक फ़ाइलों में शामिल नहीं हो सकता। मैं वर्तमान में पूरी फाइल में पढ़ता हूं, सूची को deserialize, सूची में संलग्न, सूची को फिर से क्रमबद्ध, और फ़ाइल में वापस लिखना। हालांकि, मेरा डेटा सेट बड़ा हो रहा है और मैं स्मृति से बाहर निकलना शुरू कर रहा हूं। मैं शायद कुछ जगह हासिल करने के लिए अपने डेटा संरचनाओं को अनबॉक्सिंग कर सकता हूं, लेकिन वह दृष्टिकोण स्केल नहीं करता है।हास्केल

प्रारंभिक गणना बदलने के लिए फ़ाइल प्रारूप के साथ एक समाधान नीचे और गंदा होना होगा, फिर बस मेरे तत्वों को संलग्न करें। लेकिन यह बहुत संतोषजनक नहीं है, अबास्ट्रक्शन को तोड़ने के परिणामस्वरूप फ़ाइल स्वरूप में भविष्य में बदलावों के प्रति संवेदनशील होने का उल्लेख नहीं करना चाहिए। Iteratees/Enumerators यहाँ एक आकर्षक विकल्प के रूप में दिमाग में आते हैं। मैंने उन्हें एक बाइनरी क्रमबद्धता के साथ संयोजन करने वाली लाइब्रेरी की तलाश की, लेकिन कुछ भी नहीं मिला। किसी को पता है कि यह पहले से ही किया गया है? यदि नहीं, तो इसके लिए एक पुस्तकालय उपयोगी होगा? या क्या मैं कुछ न कुछ भूल रहा हूं?

+0

क्या आप बाइनरी के लिए स्ट्रीमिंग उदाहरण लिख सकते हैं? एक चंक-वार (उत्सुक) एन्कोडर लिखना अपेक्षाकृत आसान है, जो एक समय में * एन * तत्वों के सेट लिखता है। –

+0

ओह, मैं इस बात से असहमत नहीं हूं कि यह काफी सरल है। मैं मुख्य रूप से जानना चाहता हूं कि यह पहले से ही किया जा चुका है या नहीं। यदि नहीं, तो क्या मौजूदा abstractions या टाइप कक्षाएं हैं जिन्हें मुझे शुरू करना चाहिए? – mightybyte

+0

ऐसा कोई नहीं है जिसे मैं जानता हूं। यद्यपि आपको हैकेज पर तलाशने योग्य फ़ाइल एपीआई की तलाश में भाग्य हो सकता है। –

उत्तर

6

तो मैं Data.Binary के साथ छड़ी कहता हूं लेकिन बढ़ने योग्य सूचियों के लिए एक नया उदाहरण लिखता हूं। यहाँ वर्तमान (सख्त) उदाहरण है:

instance Binary a => Binary [a] where 
    put l = put (length l) >> mapM_ put l 
    get = do n <- get :: Get Int 
       getMany n 

-- | 'getMany n' get 'n' elements in order, without blowing the stack. 
getMany :: Binary a => Int -> Get [a] 
getMany n = go [] n 
where 
    go xs 0 = return $! reverse xs 
    go xs i = do x <- get 
       x `seq` go (x:xs) (i-1) 
{-# INLINE getMany #-} 

अब, आप (बाइनरी में) स्ट्रीम एक फ़ाइल को संलग्न करने के लिए उत्सुक या आलसी अवश्य होना चाहिए कि देता है कि एक संस्करण। आलसी संस्करण सबसे तुच्छ है। कुछ ऐसा:

import Data.Binary 

newtype Stream a = Stream { unstream :: [a] } 

instance Binary a => Binary (Stream a) where 

    put (Stream [])  = putWord8 0 
    put (Stream (x:xs)) = putWord8 1 >> put x >> put (Stream xs) 

    get = do 
     t <- getWord8 
     case t of 
      0 -> return (Stream []) 
      1 -> do x   <- get 
        Stream xs <- get 
        return (Stream (x:xs)) 

मालिश उचित स्ट्रीमिंग के लिए काम करता है। अब, चुपचाप संलग्न करने के लिए, हमें फ़ाइल के अंत की तलाश करने में सक्षम होना चाहिए, और अधिक तत्व जोड़ने से पहले अंतिम 0 टैग को ओवरराइट करना होगा।

+0

हम्म, इस एन्कोडिंग के लिए ठीक से काम करने लगता है, लेकिन वास्तव में डिकोडिंग स्ट्रीमिंग होने के लिए (यह मेरे लिए में पूरे इनपुट पढ़ता है, जब मैं सिर्फ पहली प्रविष्टि इस्तेमाल करने की कोशिश) प्रतीत नहीं होता – gatoatigrado

1

इस प्रश्न का उत्तर देने के चार साल बाद, लेकिन मैं डॉन स्टीवर्ट के जवाब पर टिप्पणी में gatoatigrado के समान समस्याओं में भाग गया। put विधि विज्ञापन के रूप में काम करता है, लेकिन get पूरे इनपुट को पढ़ता है। मेरा मानना ​​है कि समस्या केस स्टेटमेंट, Stream xs <- get में पैटर्न मैच में निहित है, जो निर्धारित करना चाहिए कि शेष getStream a है या वापस लौटने से पहले नहीं।

मेरे समाधान एक प्रारंभिक बिंदु के रूप Data.Binary.Get में उदाहरण का इस्तेमाल किया:

import Data.ByteString.Lazy(toChunks,ByteString) 
import Data.Binary(Binary(..),getWord8) 
import Data.Binary.Get(pushChunk,Decoder(..),runGetIncremental) 
import Data.List(unfoldr) 

decodes :: Binary a => ByteString -> [a] 
decodes = runGets (getWord8 >> get) 

runGets :: Get a -> ByteString -> [a] 
runGets g = unfoldr (decode1 d) . toChunks 
    where d = runGetIncremental g 

decode1 _ [] = Nothing 
decode1 d (x:xs) = case d `pushChunk` x of 
        Fail _ _ str -> error str 
        Done x' _ a -> Just (a,x':xs) 
        [email protected](Partial _) -> decode1 k xs 

नोट को पढ़ने के लिए getWord8 के उपयोग यह वह जगह है इनकोडिंग [] और : स्ट्रीम के लिए put की परिभाषा से उत्पन्न उदाहरण। यह भी ध्यान दें, क्योंकि GetWord8 एन्कोडेड [] और प्रतीकों को अनदेखा करता है, यह कार्यान्वयन सूची के अंत का पता नहीं लगाएगा। मेरी एन्कोडेड फ़ाइल केवल एक ही सूची थी, इसलिए यह इसके लिए काम करती है, लेकिन अन्यथा आपको संशोधित करने की आवश्यकता होगी।

किसी भी मामले में, यह decodes सिर और अंतिम तत्वों तक पहुंचने के दोनों मामलों में निरंतर स्मृति में भाग गया।