2012-02-23 10 views
25

इस कार्यक्रम के उत्पादन मैं उम्मीद जब पाठ का कोई इनपुट फ़ाइल \ N द्वारा सीमांकित दिया पैदा करता है:withFile बनाम OpenFile

import System.IO 

main :: IO() 
main = do h <- openFile "test.txt" ReadMode 
      xs <- getlines h 
      sequence_ $ map putStrLn xs 

getlines :: Handle -> IO [String] 
getlines h = hGetContents h >>= return . lines 

OpenFile के लिए withFile प्रतिस्थापन और थोड़ा उलटफेर

import System.IO 

main :: IO() 
main = do xs <- withFile "test.txt" ReadMode getlines 
      sequence_ $ map putStrLn xs 

getlines :: Handle -> IO [String] 
getlines h = hGetContents h >>= return . lines 

मेरे द्वारा प्रबंधित तक बिल्कुल कोई आउटपुट प्राप्त करने के लिए। मैं उलझन में हूं।

संपादित करें: अब और नहीं फंस गया: विचारशील और विचार-विमर्श करने वाले उत्तरों के लिए एक और सभी के लिए धन्यवाद। मैंने प्रलेखन में थोड़ा और पढ़ा और सीखा कि के साथ फ़ाइलब्रैकेट के आंशिक अनुप्रयोग के रूप में समझा जा सकता है।

import System.IO 

main :: IO() 
main = withFile "test.txt" ReadMode $ \h -> getlines h >>= mapM_ putStrLn 

getlines :: Handle -> IO [String] 
getlines h = lines `fmap` hGetContents h 
+5

मुझे उत्तर देने के लिए अप्रासंगिक युक्ति आपको पकड़ने के लिए: 'sequence_। मानचित्र 'को' mapM_' के रूप में आसानी से लिखा जा सकता है। – So8res

+5

एक और अप्रासंगिक युक्ति: 'foo >> = वापसी।बार' बेहतर लिखा है 'एफएमएपी बार फू'; मैं विशेष रूप से इनफिक्स एफएमएपी समानार्थी का आनंद लेता हूं: 'बार <$> foo' (' आयात नियंत्रण। अनुप्रयोग 'की आवश्यकता है) –

उत्तर

29

फ़ाइल बहुत जल्दी बंद कर दिया जा रहा है:

यह है कि मैं क्या साथ समाप्त हो गया है। documentation से:

संभाल withFile

से बाहर निकलने पर बंद कर दिया इसका मतलब यह है फ़ाइल withFile समारोह रिटर्न जैसे ही बंद कर दिया जाएगा किया जाएगा।

क्योंकि hGetContents और दोस्तों के आलसी हैं, यह फ़ाइल को पढ़ने की कोशिश करेंगे नहीं है जब तक यह putStrLn साथ मजबूर किया जाता है, लेकिन तब तक, withFile पहले से ही फाइल बंद कर दी है।

समस्या को हल करने के लिए, withFile को पूरी बात पारित:

main = withFile "test.txt" ReadMode $ \handle -> do 
      xs <- getlines handle 
      sequence_ $ map putStrLn xs 

यह काम करता है, क्योंकि समय withFile फ़ाइल को बंद करने के लिए चारों ओर हो जाता है से, आप पहले से ही यह मुद्रित होता।

+0

' xs <- getlines हैंडल 'होना चाहिए। – So8res

+7

हास्केल में सभी आईओ आलसी नहीं हैं। बस आईओ जिसे इस तरह कार्यान्वित किया गया है, जिसमें 'एचजीएटीकंट्स' शामिल है। – Carl

+0

धन्यवाद - तय है। –

7

वे पूरी तरह से अलग चीजें करते हैं। openFile एक फ़ाइल को खोलता है और एक फ़ाइल हैंडल रिटर्न:

openFile :: FilePath -> IOMode -> IO Handle 

withFile एक आईओ गणना है कि एक फ़ाइल संभाल लेता रैप करने के लिए प्रयोग किया जाता है, यह सुनिश्चित करना कि संभाल बाद में बंद कर दिया है:

withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r 

आपके मामले में , withFile का उपयोग कर इस प्रकार दिखाई देगा:

main = withFile "test.txt" ReadMode $ \h -> do 
     xs <- getlines h 
     sequence_ $ map putStrLn xs 

संस्करण आप अभी फ़ाइल को खोलने होगा, फोन getlines, फिर फ़ाइल बंद करें। चूंकि getlines आलसी है, फ़ाइल खोलने के दौरान यह किसी आउटपुट को नहीं पढ़ेगा, और एक बार फ़ाइल बंद हो जाने पर, यह नहीं हो सकता है।

5

आप आलसी आईओ के सामान्य बाधाओं में भाग रहे हैं ... आलसी आईओ एक उत्कृष्ट विचार की तरह लगता है, जब तक आप उन भयानक समस्याओं को शुरू नहीं करते हैं।

यह नहीं कि आपका विशेष मामला एक अनुभवी हास्केलर को लाल हेरिंग नहीं होगा: यह टेक्स्टबुक उदाहरण है कि आलसी आईओ एक समस्या क्यों है।

main = do xs <- withFile "test.txt" ReadMode getlines 
      sequence_ $ map putStrLn xs 

withFile एक FilePath, एक विधा है और हैंडल इस विधा के साथ इस filepath खोलने से उत्पन्न से कोई लेना देना कोई ऐसा कार्य करता। फ़ाइल के साथ दिलचस्प हिस्सा यह है कि इसे फ़ाइल के मुकाबले अपवाद के मामले में ब्रैकेट और गारंटी के साथ लागू किया गया है, हैंडल निष्पादन पर कार्रवाई के बाद बंद हो जाएगा। यहां समस्या यह है कि प्रश्न में कार्रवाई (getLines) फ़ाइल को बिल्कुल नहीं पढ़ती है! यह केवल ऐसा करने का वादा करता है जब सामग्री की वास्तव में आवश्यकता होती है! यह आलसी आईओ है (असुरक्षितइंटरलेवियो के साथ कार्यान्वित, अनुमान लगाएं कि "असुरक्षित" भाग का अर्थ क्या है ...)। निश्चित रूप से इस सामग्री के आवश्यक (putStrLn) है, हैंडल को वादा के साथ बंद कर दिया गया था।

तो तुम कई समाधान हैं: आप खुले और स्पष्ट रूप से करीब का उपयोग करें (और त्यागना अपवाद-सुरक्षा) सकता है, या आप आलसी आईओ का उपयोग लेकिन गुंजाइश withFile द्वारा संरक्षित में फ़ाइल की सामग्री को छू हर क्रिया डाल सकता है:

main = withFile "test.txt" ReadMode$ \h -> do 
     xs <- getlines h 
     mapM_ putStrLn xs 

इस मामले में, यह बहुत भयानक नहीं है, लेकिन आपको यह देखना चाहिए कि समस्या अधिक परेशान हो सकती है, अगर सामग्री की आवश्यकता होने पर आप अनदेखा कर सकते हैं। एक बड़े और जटिल कार्यक्रम में आलसी आईओ तेजी से बहुत परेशान हो सकता है, और जब खुली हैंडल संख्याओं पर और सीमा पर ध्यान देना शुरू हो जाता है ... यही कारण है कि हास्केल समुदाय का नया खेल स्ट्रीमिंग सामग्री की समस्या के समाधान के साथ आना है (स्मृति में पूरी फाइलों को पढ़ने के बजाय जो आलसी आईओ के बिना कभी-कभी असंभव स्तर तक स्मृति उपयोग को उबालने की लागत पर "हल" करती है)। एक समय ऐसा लगता था जैसे इटरेटे मानक समाधान बनने जा रहा था, लेकिन अनुभवी हास्केलर के लिए भी यह बहुत ही जटिल और समझना मुश्किल था, इसलिए अन्य उम्मीदवार हाल ही में तैयार हो गए हैं: वर्तमान में सबसे आशाजनक या कम से कम सफल "conduit" हो।

+3

यह भी देखें: [आलसी आईओ के बारे में इतना बुरा क्या है?] (Http://stackoverflow.com/questions/5892653/whats-so-bad-about-lazy-i-o) –

3

जैसा कि अन्य ने उल्लेख किया है, hGetContents आलसी है। हालांकि, आप अगर आप ऐसा इच्छा कठोरता जोड़ सकते हैं:

import Control.DeepSeq 

forceM :: (NFData a, Monad m) => m a -> m a 
forceM m = do 
    val <- m 
    return $!! val 

main = do xs <- withFile "text.txt" ReadMode (forceM . getlines) 
      ... 

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

यदि आपको संसाधनों के अधिक सुदृढ़ नियंत्रण की आवश्यकता है, तो आपको ResourceT (जो conduit पैकेज के साथ आता है) या इसी तरह का उपयोग करना चाहिए।

[संपादित करें: सुनिश्चित करें कि पूरे मूल्य मजबूर किया जाता है बनाने के लिए Control.DeepSeq (बजाय $!) से $!! का उपयोग करें। टिप के लिए धन्यवाद, @benmachine]

+0

क्या आपने यह जांच लिया कि यह वास्तव में काम करता है? मुझे दो संभावित समस्याएं दिखाई देती हैं: 1. वास्तव में एक समस्या जरूरी नहीं है, लेकिन आईओ के संबंध में चीजों का मूल्यांकन कब किया जाता है, इस बारे में तर्क करना आसान है यदि आप ''!! "के बजाय' Control.Exception.evaluate 'का उपयोग करते हैं। एक और गंभीर समस्या, '$! 'केवल वैल के पहले कन्स्ट्रक्टर का मूल्यांकन करेगी, इसलिए आप केवल एक पंक्ति लाएंगे; आप वास्तव में एक गहरी सीक की तरह कुछ और महत्वपूर्ण चाहते हैं, या बेहतर अभी भी गैर आलसी आईओ। माना जाता है कि मैंने इसे जांच नहीं किया/काम नहीं किया है, लेकिन मुझे संदेह है :) –

+0

@benmachine मैंने जांच की (ओपी द्वारा दिए गए शेष कोड के साथ), और यह वास्तव में काम करता है (मैंने इसे छोटे से चेक किया 5 लाइनों के साथ परीक्षण फ़ाइल)। यह ('बल एम। गेटलाइन) 'के स्थान पर' (मूल्यांकन <=

+0

@benmachine आपके संदेह बड़ी फ़ाइलों के लिए सही हैं; 'bash> wc -l test.txt' =' 54730 test.txt'। 'bash> runhaskell lazyio.hs | wc -l' = '228 '। अजीब, मैंने सोचा कि 'लाइनबफरिंग' डिफ़ॉल्ट था। –

11

उह, क्या कोई भी कभी भी सरल समाधान नहीं देता?

main :: IO() 
main = do xs <- fmap lines $ readFile "test.txt" 
      mapM_ putStrLn xs 

का उपयोग openFile + hGetContents या withFile + hGetContents जब तुम सिर्फ readFile का उपयोग कर सकते है। readFile के साथ आप फ़ाइल को बहुत जल्दी बंद करके पैर में खुद को शूट नहीं कर सकते हैं।

+0

न ही, वास्तव में, क्या आप फ़ाइल को बंद कर सकते हैं :) यदि आप सावधान नहीं हैं तो आप इस तरह से फ़ाइल हैंडल से बाहर हो जाएंगे। वैसे भी, यदि आप कुछ और उन्नत करना चाहते हैं (जैसे बफर मोड, या जो भी सेट करना चाहते हैं) तो 'withFile' जाने का तरीका है। –

+5

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

+0

लेकिन सवाल यह नहीं था कि "मैं फाइलें कैसे पढ़ूं?" यह था "मैं 'फ़ाइल' के साथ कैसे उपयोग करूं?" या अधिक विशेष रूप से "इस आश्चर्यजनक तरीके से 'फ़ाइल' के साथ क्यों व्यवहार कर रहा है?"। मुझे लगता है कि इस तरह के एक प्रश्न की अपेक्षा करने के लिए मूर्खतापूर्ण है * * * * के बारे में एक विस्तृत स्पष्टीकरण के साथ आओ * क्यों * प्रश्नकर्ता को 'फ़ाइल' के साथ उपयोग करने की आवश्यकता है। बहुत अच्छे कारण हैं। –

संबंधित मुद्दे