haskell - हस्केले आलसी बायटेस्ट्रिंग+लिखने/प्रगति कार्य लिखना




io progress (2)

मैं हास्केल लाज़ी आईओ को झुका रहा हूं

मैं प्रतिलिपि की प्रगति को सांत्वना देने के दौरान एक बड़ी फ़ाइल (8 जीबी) की प्रतिलिपि बनाने के लिए एक शानदार तरीका तलाश रहा हूं।

निम्नलिखित सरल प्रोग्राम पर विचार करें जो एक फ़ाइल को चुपचाप प्रतिलिपि बनाता है

module Main where

import System
import qualified Data.ByteString.Lazy as B

main = do [from, to] <- getArgs
          body <- B.readFile from
          B.writeFile to body

Imgine एक कॉलबैक समारोह है जो आप रिपोर्टिंग के लिए उपयोग करना चाहते हैं:

onReadBytes :: Integer -> IO ()
onReadBytes count = putStrLn $ "Bytes read: " ++ (show count)

प्रश्न: आलसी बाइट स्ट्रिंग में रीडबॉइट फ़ंक्शन को बुनाई कैसे करें, तो इसे सफलतापूर्वक पढ़े जाने पर वापस बुलाया जाएगा? या अगर यह डिज़ाइन अच्छा नहीं है, तो हास्केल को ऐसा करने का तरीका क्या है?

नोट: कॉलबैक की आवृत्ति महत्वपूर्ण नहीं है, इसे हर 1024 बाइट या हर 1 एमबी कहा जा सकता है - महत्वपूर्ण नहीं

उत्तर: उत्तर के लिए कैमकैन के लिए बहुत धन्यवाद। मैं इसे पूरी तरह से पढ़ने का सुझाव देता हूं

ब्लेक, कोड का मेरा संस्करण है कैमकैन के कोड के आधार पर, आप इसे उपयोगी पा सकते हैं

module Main where

import System
import System.IO
import qualified Data.ByteString.Lazy as B

main = do [from, to] <- getArgs
          withFile from ReadMode $ \fromH ->
            withFile to WriteMode $ \toH ->
              copyH fromH toH $ \x -> putStrLn $ "Bytes copied: " ++ show x

copyH :: Handle -> Handle -> (Integer -> IO()) -> IO ()
copyH fromH toH onProgress =
    copy (B.hGet fromH (256 * 1024)) (write toH) B.null onProgress
    where write o x  = do B.hPut o x
                          return . fromIntegral $ B.length x

copy :: (Monad m) => m a -> (a -> m Integer) -> (a -> Bool) -> (Integer -> m()) -> m()
copy = copy_ 0

copy_ :: (Monad m) => Integer -> m a -> (a -> m Integer) -> (a -> Bool) -> (Integer -> m()) -> m()
copy_ count inp outp done onProgress = do x <- inp
                                          unless (done x) $
                                            do n <- outp x
                                               onProgress (n + count)
                                               copy_ (n + count) inp outp done onProgress

सबसे पहले, मैं यह नोट करना चाहूंगा कि हस्केल प्रोग्रामर की एक उचित संख्या में आम तौर पर आलसी आईओ का संबंध कुछ संदेह के साथ होता है। यह तकनीकी रूप से पवित्रता का उल्लंघन करती है, लेकिन एक सीमित तरीके से (जहां तक ​​मुझे जानकारी है) निरंतर इनपुट पर एक प्रोग्राम चलाने पर ध्यान देने योग्य नहीं है [0] दूसरी तरफ, बहुत से लोगों को इसके साथ ठीक किया जाता है, फिर क्योंकि इसमें केवल एक बहुत ही सीमित प्रकार की अशुद्धता शामिल है

एक आलसी डेटा ढांचे का भ्रम बनाने के लिए जो वास्तव में ऑन-डिमांड I / O के साथ बनाया गया है, readFile जैसी फ़ंक्शन, पर्दे के पीछे चुपके वाले readFile का उपयोग करके कार्यान्वित की readFile हैं। ऑन-डिमांड I / O में बुन करना फ़ंक्शन के निहित है, और यह बहुत ही इसी कारणों के लिए वास्तव में एक्स्टेंसिबल नहीं है कि एक नियमित ByteString प्राप्त करने का भ्रम समझना है।

विवरण को छिपाते हुए और छद्मोडो को लिखना, जैसे कुछ readFile मूल रूप से इस तरह काम करता है:

lazyInput inp = lazyIO (lazyInput' inp)
lazyInput' inp = do x <- readFrom inp
                    if (endOfInput inp)
                        then return []
                        else do xs <- lazyInput inp
                                return (x:xs)

... जहां हर बार lazyIO कहा जाता है, यह वास्तव में उपयोग किए जाने तक I / O की lazyIO है जब भी वास्तविक रीडिंग होता है, हर बार आपके रिपोर्टिंग फ़ंक्शन को आह्वान करने के लिए आपको सीधे इसे बुनाई की आवश्यकता होती है, और इस तरह के एक फ़ंक्शन का एक सामान्यीकृत संस्करण लिखा जा सकता है, मेरे ज्ञान में कोई भी मौजूद नहीं है

उपरोक्त को देखते हुए आपके पास कुछ विकल्प हैं:

  • आलसी I / O फ़ंक्शंस के कार्यान्वयन को देखें जो आप उपयोग कर रहे हैं, और स्वयं को लागू करें जिसमें प्रगति रिपोर्टिंग फ़ंक्शन शामिल है अगर यह एक गंदा हैक की तरह लगता है, इसलिए यह बहुत सुंदर है, लेकिन वहां आप जाते हैं

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

  • आलसी आलसी I / O को छोड़ दें और सादे पुरानी नियमित I / O पर स्विच करें: एक IO कार्रवाई लिखें जो एक टुकड़ा पढ़ता है, रिपोर्टिंग जानकारी प्रिंट करता है, और जितना इनपुट कर सकता है उतना इनपुट करता है; तब तक एक पाश में इसे शुरू किया जब तक किया। आप इनपुट के साथ क्या कर रहे हैं और आप अपने प्रसंस्करण में आलस्य पर कितना निर्भर कर रहे हैं इसके आधार पर, इसमें परिमित-राज्य-मशीन स्ट्रीम प्रोसेसर के एक समूह का निर्माण करने और 90 प्राप्त करने के लिए कुछ लगभग-तुच्छ कार्यों को लिखने से कुछ भी शामिल हो सकता है Iteratees reinventing के रास्ते का%

[0] : यहाँ अंतर्निहित कार्य को unsafeInterleaveIO कहा जाता है, और सबसे अच्छा ज्ञान के लिए केवल अशुद्धता का पालन करने का एकमात्र तरीका प्रोग्राम को अलग-अलग इनपुट पर चलाने की आवश्यकता होती है (जिस स्थिति में यह किसी भी तरह से अलग व्यवहार करने का हकदार है, यह हो सकता है ऐसे तरीकों से जो शुद्ध कोड में समझ नहीं आते हैं), या कुछ खास तरीके से कोड बदलते हैं (यानी, रिफैक्चरिंग जिसका कोई प्रभाव नहीं होना चाहिए गैर-स्थानीय प्रभाव हो सकता है)

अधिक सुगम कार्यों का उपयोग करते हुए, "सादे पुराना नियमित I / O" तरीके से काम करने का एक बहुत अच्छा उदाहरण है:

import System
import System.IO
import qualified Data.ByteString.Lazy as B

main = do [from, to] <- getArgs
          -- withFile closes the handle for us after the action completes
          withFile from ReadMode $ \inH ->
            withFile to WriteMode $ \outH ->
                -- run the loop with the appropriate actions
                runloop (B.hGet inH 128) (processBytes outH) B.null

-- note the very generic type; this is useful, because it proves that the
-- runloop function can only execute what it's given, not do anything else
-- behind our backs.
runloop :: (Monad m) => m a -> (a -> m ()) -> (a -> Bool) -> m ()
runloop inp outp done = do x <- inp
                           if done x
                             then return ()
                             else do outp x
                                     runloop inp outp done

-- write the output and report progress to stdout. note that this can be easily
-- modified, or composed with other output functions.
processBytes :: Handle -> B.ByteString -> IO ()
processBytes h bs | B.null bs = return ()
                  | otherwise = do onReadBytes (fromIntegral $ B.length bs)
                                   B.hPut h bs

onReadBytes :: Integer -> IO ()
onReadBytes count = putStrLn $ "Bytes read: " ++ (show count)

"128" ऊपर एक बार में पढ़ने के लिए कितने बाइट हैं यह मेरे "स्टैक ओवरफ़्लो स्निपेट्स" निर्देशिका में एक यादृच्छिक स्रोत फ़ाइल पर चल रहा है:

$ runhaskell ReadBStr.hs Corec.hs temp
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 128
Bytes read: 83
$





bytestring