2013-10-13 4 views
16

की प्रगति दिखाएं मेरे पास कुछ ऑब्जेक्ट्स के साथ हास्केल में सूची है। और मुझे यह पता लगाना होगा कि इनमें से कोई वस्तु कुछ शर्त को संतुष्ट करती है या नहीं।हास्केल प्रोग्राम

any (\x -> check x) xs 

लेकिन समस्या यह है कि चेक आपरेशन बहुत महंगा है, और सूची काफी बड़ी है: तो, मैं निम्नलिखित लिखा था। मैं रनटाइम में वर्तमान प्रगति देखना चाहता हूं, उदाहरण के लिए 50% (1000/2000 checked).
मैं यह कैसे कर सकता हूं?

+0

यदि आप बड़े डेटा सेट के साथ कुछ भारी उठाने कर रहे हैं, तो सूची का उपयोग न करें। सूची सीखने के लिए अच्छी है लेकिन असली दुनिया ऐप – Ankur

+0

में बुरी तरह से प्रदर्शन करती है, साथ ही, कोई भी स्थिति वापस आने वाले तत्व को प्राप्त करने के बाद वापस आ जाएगा, उस स्थिति में यह दिखाने की प्रगति है कि कुल तत्वों से कितने तत्व संसाधित होते हैं, यह समझ में नहीं आता है – Ankur

+0

@ अंकुर यह समझ में आता है, क्यों नहीं? इसका अर्थ यह हो सकता है कि '30% तत्वों को संसाधित किया गया है और कुछ भी उपयुक्त नहीं पाया गया है ' – pfedotovsky

उत्तर

16

चूंकि आप अपने फ़ंक्शन की प्रगति देखना चाहते हैं (जो फ़ंक्शन का दुष्प्रभाव है) सबसे स्पष्ट समाधान मोनैड का उपयोग करना है। ऐसा करने के लिए पहली बात यह है any समारोह की एक monadic संस्करण बनाने के लिए है:

anyM :: (Monad m) => (a -> m Bool) -> [a] -> m Bool 
anyM _ []  = return False 
anyM pred (x:xs) = reduce (pred x) xs 
    where reduce acc []  = acc 
      reduce acc (x:xs) = do 
       condition <- acc 
       if condition 
        then return condition 
        else reduce (pred x) xs 

ऊपर समारोह anyMany समारोह की एक monadic संस्करण है। यह हमें यह जांचने के अलावा दुष्प्रभाव उत्पन्न करने की अनुमति देता है कि दी गई सूची में कोई भी वस्तु दी गई भविष्यवाणी को पूरा करती है या नहीं।

anyVar :: (a -> Bool) -> [a] -> IO Bool 
anyVar pred xs = anyM check $ zip [1..] xs 
    where check (n,x) = do 
      putStrLn $ show n ++ " checked. " 
      return $ pred x 

सूचना है कि क्योंकि हम लंबाई पता नहीं है:

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

anyFix :: (a -> Bool) -> Int -> [a] -> IO Bool 
anyFix pred length xs = anyM check $ zip [1..] xs 
    where check (n,x) = do 
      putStrLn $ show (100 * n `div` length) ++ "% (" ++ 
       show n ++ "/" ++ show length ++ " checked). " 
      return $ pred x 

अनंत सूचियों के लिए और सूचियों जिसकी लंबाई आप पहले से पता नहीं है के लिए anyVar समारोह का उपयोग करें। सीमित सूचियों के लिए anyFix फ़ंक्शन का उपयोग करें जिनकी लंबाई आप पहले से जानते हैं।

यदि सूची बड़ी है और आप पहले सूची की लंबाई नहीं जानते हैं तो length फ़ंक्शन को इसकी लंबाई निर्धारित करने के लिए संपूर्ण सूची को पार करने की आवश्यकता होगी। इसलिए इसके बजाय anyVar का उपयोग करना बेहतर होगा।

अंत में रैप करने के लिए यह यह सब कैसे आप ऊपर कार्यों का उपयोग होता है:

main = anyFix (==2000) 2000 [1..2000] 

आपके मामले में आप के बजाय निम्न कर सकता है:

main = anyVar check xs 

आशा इस जवाब आप मदद की।

+1

उत्कृष्ट उत्तर लेकिन आपका कोई भी दो तत्वों के लिए दो बार खराब होने की जांच करता है: पहला यदि चेक महंगा है तो हम अपने काम की मात्रा को दोगुना नहीं करना चाहते हैं, दूसरा यह मोनैडिक है, यह नोटिस कर सकता है कि इसे दो बार कहा जाता है , उदाहरण के लिए एक राज्य यह ट्रैक करने के लिए प्रयोग किया जाता था कि कितने तत्वों को संभाला गया था भ्रमित हो जाएगा। यदि स्थिति हो तो बेहतर तरीके से अपना स्थान बदलें, फिर स्थिति को वापस करें (पूर्व x) xs' को कम करें। – Jedai

+0

@ जेडाई नाइस कैच। मैंने इसे पहले कभी नहीं देखा। शायद क्योंकि "\ ESC [2K \ ESC [0G" ने हर बार लाइन मिटा दी। मैं उलझन में हूं - यह दो बार क्यों निष्पादित करता है? एक स्पष्टीकरण महान होगा। कुछ कारणों से इसके अलावा मेरा 'AnyVar' और' anyFix' फ़ंक्शन 'putStr' पर एक पार्स त्रुटि बढ़ाते हैं। हालांकि अगर मैं 'put'tr" स्ट्रिंग के साथ 'डू' ब्लॉक को प्रतिस्थापित करता हूं ">> वापसी (पूर्व x) 'तो यह अपेक्षा के अनुसार काम करता है। क्या आपको पता चलेगा कि ऐसा क्यों होता है? –

+0

@ जेडाई मैंने यह पता लगाया कि क्यों 'AnyVar' और' AnyFix 'एक पार्स त्रुटि उठा रहे थे: यह गलत इंडेंटेशन के कारण था। मैं अभी भी सोच रहा हूं कि क्यों मेरे मूल 'anyM' फ़ंक्शन ने प्रत्येक आइटम के लिए दो बार' pred' का मूल्यांकन किया। –

7

सबसे अनुभवहीन और सीधा रास्ता लागू करने के लिए है अपने स्वयं के

anyM :: (a -> Bool) -> [a] -> IO Bool 

कि प्रगति बार प्रिंट (जैसे terminal-progress-bar का उपयोग)।

लेकिन ध्यान दें कि प्रतिशत की गणना करने के लिए, आपको पूरी सूची का मूल्यांकन करना होगा। यह आलस्य टूटता है और कार्यक्रम के अंतरिक्ष व्यवहार पर खराब और अवांछित प्रभाव हो सकता है।

भी है कि आप (जैसे any के रूप में) एक शुद्ध गणना की निगरानी एक उदाहरण के लिए bytestring-progress देखने की अनुमति देता unsafePerformIO और unsafeInterleaveIO का उपयोग कर दृष्टिकोण हैं। लेकिन यह संदिग्ध डिज़ाइन है कि आपको केवल तभी उपयोग करना चाहिए यदि आप जानते हैं कि आप परिणामों को समझते हैं।

11

ऐसा करने का एक और तरीका स्ट्रीमिंग लाइब्रेरी का उपयोग कर रहा है जैसे conduit या pipes। , आप अगर

import Pipes 
import qualified Pipes.Prelude as P 

bigList :: [Int] 
bigList = [1,2,3,4] 

check :: Int -> Bool 
check = (>3) 

main :: IO() 
main = do 
    result <- P.any check $ each bigList >-> P.chain (\_ -> putStrLn ".") 
    putStrLn . show $ result 

अब यहाँ कुछ नमूना है जो एक डॉट हर बार सूची का एक तत्व जांच की जानी आता प्रिंट pipes का उपयोग कर कोड, है (each पाइप्स मॉड्यूल से एक समारोह है।) प्रतिशत दिखाने के लिए चाहता था, P.chain (\_ -> putStrLn ".") पाइपलाइन का हिस्सा थोड़ा अधिक स्मार्ट होना चाहिए। इसे वर्तमान प्रतिशत को राज्य के रूप में ले जाना होगा, और सूची की लंबाई पता होना चाहिए। (। यदि आपकी सूची विशाल और lazily उत्पन्न है, इसकी लंबाई की गणना के अपने मूल्यांकन के लिए मजबूर है और संभवत: समस्या का कारण बन आप पहले से ही स्मृति में है, तो यह नहीं बड़ी समस्या होगी।)

संपादित करें: यहाँ

{-# LANGUAGE FlexibleContexts #-} 

import Pipes 
import qualified Pipes.Prelude as P 
import Data.Function 
import Control.Monad.RWS 

bigList :: [Int] 
bigList = [1,2,3,4] 

check :: Int -> Bool 
check = (>3) 

-- List length is the environment, number of received tasks is the state. 
tracker :: (MonadReader Int m, MonadState Int m, MonadIO m) => Pipe a a m r 
tracker = P.chain $ \_ -> do 
    progress <- on (/) fromIntegral `liftM` (modify succ >> get) `ap` ask 
    liftIO . putStrLn . show $ progress 

main :: IO() 
main = do 
    (result,()) <- evalRWST (P.any check $ each bigList >-> tracker) 
          (length bigList) -- list length as unchanging environment 
          0 -- initial number of received tasks (the mutable state) 
    putStrLn . show $ result 

आगे यह भी केवल महत्वपूर्ण प्रतिशत बढ़ जाती है को दिखाने के लिए परिष्कृत हो सकता है: पिछले कोड का एक संभव विस्तार है कि वास्तव में प्रतिशत से पता चलता है।

+0

मुझे सूची की लंबाई पता है, इसलिए यह कोई समस्या नहीं है। – pfedotovsky

+1

यदि आप लंबाई जानते हैं, तो एक ऐरे एक सूची से बेहतर विकल्प प्रतीत होता है। –

3

मैं सिर्फ Debug.Trace.trace का उपयोग और इतने की तरह वर्तमान स्थिति का ट्रैक रखने होगा:

any (\(i,x) -> trace (showProgress i (length xs)) $ check x) $ zip [1..] xs 
+0

'trace' एक हैक है जो 'unsafePerformIO' पर निर्भर करता है। चूंकि मॉड्यूल नाम से पता चलता है कि यह केवल डीबगिंग के लिए है। कभी भी उत्पादन कोड के लिए इसका इस्तेमाल न करें! – Lemming

0

आप explicit-exception:Control.Monad.Execption.Synchronous या transformers:Control.Monad.Trans.Maybe तरह स्पष्ट अपवाद के लिए एक पुस्तकालय का उपयोग कर सकते हैं, जब आप एक तत्व पाया जाता है और "एक अपवाद फेंक" है कि चेक पास करता है।

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