2010-07-29 9 views
8

निम्नलिखित कार्यक्रम सही ढंग से समाप्त हो जाता है:हैस्केल में mapM सख्त है? इस कार्यक्रम को एक ढेर ओवरफ्लो क्यों मिलता है?

import System.Random 

randomList = mapM (\_->getStdRandom (randomR (0, 50000::Int))) [0..5000] 

main = do 
    randomInts <- randomList 
    print $ take 5 randomInts 

रनिंग:

$ runhaskell test.hs 
[26156,7258,29057,40002,26339] 

हालांकि, एक अनंत सूची के साथ यह भोजन, कार्यक्रम कभी नहीं समाप्त हो जाता है, और जब संकलित है, अंत में एक ढेर अतिप्रवाह त्रुटि देता है!

import System.Random 

randomList = mapM (\_->getStdRandom (randomR (0, 50000::Int))) [0..] 

main = do 
    randomInts <- randomList 
    print $ take 5 randomInts 

चल रहा है,

$ ./test 
Stack space overflow: current size 8388608 bytes. 
Use `+RTS -Ksize -RTS' to increase it. 

मैं इस कार्यक्रम lazily getStdRandom मूल्यांकन करने के लिए हर बार जब मैं सूची से एक आइटम लेने, तो 5 बार करने के बाद खत्म कर की उम्मीद। यह पूरी सूची का मूल्यांकन करने की कोशिश क्यों कर रहा है?

धन्यवाद।

क्या यादृच्छिक संख्याओं की अनंत सूची प्राप्त करने का कोई बेहतर तरीका है? मैं इस सूची को एक शुद्ध समारोह में पास करना चाहता हूं।

संपादित करें: कुछ और पढ़ने से पता चला समारोह

randomList r = do g <- getStdGen 
        return $ randomRs r g 

मैं के लिए क्या देख रहा था है।

EDIT2: कैमकैन के उत्तर पढ़ने के बाद, मुझे एहसास हुआ कि getStdGen प्रत्येक कॉल पर एक नया बीज प्राप्त कर रहा है। इसके बजाय, बेहतर एक सरल एक शॉट यादृच्छिक सूची जनरेटर के रूप में इस सुविधा का उपयोग करने के लिए:

import System.Random 

randomList :: Random a => a -> a -> IO [a] 
randomList r g = do s <- newStdGen 
        return $ randomRs (r,g) s 

main = do r <- randomList 0 (50::Int) 
      print $ take 5 r 

लेकिन मैं अभी भी समझ में नहीं आता क्यों मेरे mapM कॉल समाप्त नहीं किया। स्पष्ट रूप से यादृच्छिक संख्या से संबंधित नहीं है, लेकिन mapM के साथ कुछ करने के लिए शायद।

उदाहरण के लिए, मैंने पाया कि निम्नलिखित भी समाप्त नहीं करता:

randomList = mapM (\_->return 0) [0..] 

main = do 
    randomInts <- randomList 
    print $ take 50000 randomInts 

क्या देता है? वैसे, आईएमएचओ, उपर्युक्त randomInts फ़ंक्शन System.Random में होना चाहिए। यह बहुत सुविधाजनक है कि बस आईओ मोनैड में एक यादृच्छिक सूची उत्पन्न करें और आवश्यकता होने पर इसे शुद्ध कार्य में पास करें, मुझे नहीं लगता कि यह मानक पुस्तकालय में क्यों नहीं होना चाहिए।

+0

ओह, और मेरा उत्तर के परिशिष्ट के रूप: तुम बस '\ r के रूप में' randomInts' की एक अधिक सामान्य संस्करण लिख सकें -> FMAP (randomRs r) getStdGen'। इसमें टाइप '(रैंडम ए) => (ए, ए) -> आईओ [ए] 'है, दूसरे शब्दों में, यह किसी भी प्रकार के लिए रैंडम मानों की एक सूची उत्पन्न करता है जो' रैंडम 'का उदाहरण है। –

+0

यहां तक ​​कि बेहतर, धन्यवाद। – Steve

+0

मुझे लगता है कि यही कारण है कि मेरा 'यादृच्छिक सूची' फ़ंक्शन को मानक lib में भी होने की आवश्यकता नहीं होगी, अगर वह छोटा हो, लेकिन लड़का यह स्पष्ट नहीं है कि इसे एक नए के लिए कैसे लिखना है;) तो मुझे अभी भी लगता है कि यह होना चाहिए सुविधा के लिए वहाँ .. – Steve

उत्तर

5

मैं इस तरह कुछ करना होगा, दे randomRs एक प्रारंभिक RandomGen साथ काम करते हैं:

#! /usr/bin/env runhaskell 

import Control.Monad 
import System.Random 


randomList :: RandomGen g => g -> [Int] 
randomList = randomRs (0, 50000) 

main :: IO() 
main = do 
    randomInts <- liftM randomList newStdGen 
    print $ take 5 randomInts 

आलस्य का सवाल है, क्या यहाँ क्या हो रहा है कि mapM(sequence . map)

इसकी प्रकार है है: mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]

यह [m b] देकर फ़ंक्शन मैप कर रहा है और फिर m [b] बनाने के लिए उन सभी कार्रवाइयों को निष्पादित करने की आवश्यकता है। यह अनुक्रम है जो अनंत सूची के माध्यम से कभी नहीं मिलेगा।

यह एक पूर्व प्रश्न के उत्तर में बेहतर समझाया गया है: Is Haskell's mapM not lazy?

+0

धन्यवाद आप महोदय! – Steve

12

सामान्य रूप में रैंडम संख्या सख्त नहीं हैं, लेकिन monadic बाध्यकारी है - समस्या यहाँ mapM पूरी सूची क्रम नहीं है है। अपने प्रकार के हस्ताक्षर पर विचार करें, (a -> m b) -> [a] -> m [b]; के रूप में यह संकेत मिलता है, क्या यह होता है, sequence उस प्रकार m [b] का एक परिणाम प्राप्त करने के लिए सूची पहले map प्रकार [a] की सूची प्रकार [m b] की एक सूची में है तो। इसलिए, जब आप mapM, लागू करने के परिणाम को बाध्य करते हैं उदा। <- के दाएँ हाथ की ओर पर डालने, क्या इसका मतलब यह है कि "सूची पर इस समारोह के नक्शे, तो प्रत्येक monadic कार्रवाई अमल, और परिणाम एक ही सूची में वापस गठबंधन" द्वारा। यदि सूची अनंत है, तो निश्चित रूप से यह समाप्त नहीं होगा।

आप बस यादृच्छिक संख्या की एक धारा चाहते हैं, आप प्रत्येक संख्या के लिए एक इकाई का उपयोग किए बिना सूची उत्पन्न करने की जरूरत है। मुझे पूरी तरह से यकीन नहीं है कि आपने अपने डिजाइन का उपयोग क्यों किया है, लेकिन मूल विचार यह है: बीज मूल्य को देखते हुए, 1 की एक जोड़ी बनाने के लिए एक छद्म-यादृच्छिक संख्या जेनरेटर का उपयोग करें) एक यादृच्छिक संख्या 2) एक नया बीज , फिर नए बीज के साथ दोहराना। कोई भी दिया गया बीज निश्चित रूप से हर बार एक ही अनुक्रम प्रदान करेगा। तो, आप फंक्शन getStdGen का उपयोग कर सकते हैं, जो IO मोनैड में एक ताजा बीज प्रदान करेगा; फिर आप उस बीज का उपयोग पूरी तरह शुद्ध कोड में अनंत अनुक्रम बनाने के लिए कर सकते हैं।

वास्तव में, System.Random, randoms या randomRs बजाय random और randomR ठीक उस उद्देश्य के लिए काम करता है प्रदान करता है।

यदि किसी कारण से आप इसे स्वयं करना चाहते हैं, तो आप जो चाहते हैं वह आवश्यक है सामने आया। Data.List से समारोह unfoldr प्रकार हस्ताक्षर (b -> Maybe (a, b)) -> b -> [a] है, जो काफी आत्म व्याख्यात्मक है: प्रकार b के एक मूल्य को देखते हुए यह प्रकार a के दोनों कुछ और प्रकार b का एक नया जनरेटर मूल्य, या Nothing इंगित करने के लिए प्राप्त करने के लिए समारोह लागू होता है अनुक्रम का अंत

आप एक अनंत सूची चाहते हैं, इसलिए Nothing वापस जाने के लिए की जरूरत कभी नहीं होगा।

unfoldr (Just . randomR (0, 50000::Int)) :: (RandomGen a) => a -> [Int] 

... जो बिल्कुल के रूप में यह दावा करता है:: को देखते हुए एक

Just . randomR (0, 50000::Int) :: (RandomGen a) => a -> Maybe (Int, a) 

दूध पिलाने unfoldr में है कि इस देता है: इस प्रकार, आंशिक रूप से वांछित सीमा के लिए आवेदन randomR और Just साथ यह रचना इस देता है RandomGen का उदाहरण है, यह एक अनंत (और आलसी) कि बीज से उत्पन्न यादृच्छिक संख्या की सूची का उत्पादन करेगा।

+0

खुलासा! मैं कभी भी खुलासे का उपयोग क्यों नहीं करता? वे बहुत अच्छे है। – dino