2012-09-26 16 views
11

मान लीजिए, मैं मदों की बड़ी सूची हो रही है आईओ के साथ काम करते हुए:आलसी संस्करण

as <- getLargeList 
bs <- mapM fn as 

mapM है:

as <- getLargeList 

अब, मैं लागू करने के लिए fn :: a -> IO bas पर कोशिश कर रहा हूँ mapM :: Monad m => (a -> m b) -> [a] -> m [b] टाइप करें, और मुझे मिलान मिलान के संदर्भ में यही चाहिए। लेकिन यह परिणाम लौटने तक स्मृति में सभी श्रृंखला बनाता है। मैं mapM के एनालॉग की तलाश में हूं, जो आलसी काम करेगा, ताकि मैं bs के सिर का उपयोग कर सकूं जबकि पूंछ अभी भी बना रहा है।

+1

शायद इससे मदद मिलेगी। http://stackoverflow.com/questions/3270255/is-haskells-mapm-not-lazy –

+0

एंटोन, मैंने इस विषय को पढ़ा है, लेकिन मुझे जवाब नहीं मिला: क्या आलसी गणना के लिए mapM का एक विकल्प है। –

+0

@DmitryBespalov एक ही प्रकार के हस्ताक्षर के साथ नहीं। 'मोनाड' में बाद में प्रभावों को परिभाषित करने के लिए एक अमूर्तता नहीं है - और यही है कि आपको 'mapM' को आलसी होने के लिए क्या करना होगा। – Carl

उत्तर

18

उस मामले के लिए unsafeInterleaveIO या किसी आलसी आईओ का उपयोग न करें। यह ठीक है कि यह समस्या हल करने के लिए बनाई गई थी: आलसी आईओ से परहेज, जो अप्रत्याशित संसाधन प्रबंधन देता है। यह चाल कभी भी सूची का निर्माण नहीं करती है और जब तक आप इसका उपयोग नहीं कर लेते हैं तब तक इसे लगातार पुनरावृत्त करते हैं। मैं इसे प्रदर्शित करने के लिए, अपनी खुद की लाइब्रेरी, pipes से उदाहरणों का उपयोग करूंगा।

सबसे पहले, निर्धारित करें:

import Control.Monad 
import Control.Monad.Trans 
import Control.Pipe 

-- Demand only 'n' elements 
take' :: (Monad m) => Int -> Pipe a a m() 
take' n = replicateM_ n $ do 
    a <- await 
    yield a 

-- Print all incoming elements 
printer :: (Show a) => Consumer a IO r 
printer = forever $ do 
    a <- await 
    lift $ print a 

अब हमारे उपयोगकर्ता के लिए मतलब हो और मांग वे हमारे लिए बहुत बड़ी सूची का उत्पादन: अब

prompt100 :: Producer Int IO() 
prompt100 = replicateM_ 1000 $ do 
    lift $ putStrLn "Enter an integer: " 
    n <- lift readLn 
    yield n 

, चलो यह चलाते हैं:

>>> runPipe $ printer <+< take' 1 <+< prompt100 
Enter an integer: 
3<Enter> 
3 

यह केवल उपयोगकर्ता को एक पूर्णांक के लिए संकेत देता है, क्योंकि हम केवल एक पूर्णांक की मांग करते हैं!

आप getLargeList से उत्पादन के साथ prompt100 बदलना चाहते हैं, तो आप सिर्फ लिखने:

yourProducer :: Producer b IO() 
yourProducer = do 
    xs <- lift getLargeList 
    mapM_ yield xs 

... और फिर चलाएँ:

>>> runPipe $ printer <+< take' 1 <+< yourProducer 

यह lazily सूची स्ट्रीम और कभी नहीं का निर्माण करेगा स्मृति में सूची, सभी असुरक्षित IO हैक का उपयोग किए बिना। यह बदलने के लिए कि आप कितने तत्व मांगते हैं, बस take'

इस तरह के अधिक उदाहरणों के लिए, pipes tutorialControl.Pipe.Tutorial पर पढ़ें।

आलसी आईओ समस्याओं का कारण बनने के बारे में और जानने के लिए, इस विषय पर ओलेग की मूल स्लाइड पढ़ें, जिसे आप here पा सकते हैं।वह आलसी आईओ का उपयोग करने में समस्याओं को समझाने का एक अच्छा काम करता है। किसी भी समय आप आलसी आईओ का उपयोग करने के लिए मजबूर महसूस करते हैं, जो आप वास्तव में चाहते हैं वह एक इटारेट लाइब्रेरी है।

+5

+100 उत्तर के लिए धन्यवाद! और यह वास्तव में उपयोगी पुस्तकालय है, धन्यवाद! –

+2

गेब्रियल के लिए –

+0

@DmitryBespalov आपका स्वागत है! मैं हमेशा मदद करने में खुश हूं। –

6

आईओ मोनैड में प्रभाव को रोकने के लिए एक तंत्र है। इसे unsafeInterleaveIO कहा जाता है। वांछित प्रभाव प्राप्त करने के लिए आप इसका उपयोग कर सकते हैं:

import System.IO.Unsafe 

lazyMapM :: (a -> IO b) -> [a] -> IO [b] 
lazyMapM f [] = return [] 
lazyMapM f (x:xs) = do y <- f x 
         ys <- unsafeInterleaveIO $ lazyMapM f xs 
         return (y:ys) 

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

+1

मेरे दृष्टिकोण से, Haskell में System.IO.Unsafe का उपयोग करना हैक की तरह कुछ है। मैं इसके "असुरक्षित" व्यवहार के कारण इसका उपयोग करना पसंद करता हूं। हालांकि, मैं आपके उत्तर की सराहना करता हूं। पाइप लाइब्रेरी –