2009-07-15 15 views
12

मेरे पास सी में कुछ फ़ंक्शन लिखे गए हैं जिन्हें मैं हास्केल से कॉल करता हूं। ये फ़ंक्शन IO (CInt) लौटाते हैं। कभी-कभी मैं उन सभी कार्यों को चलाने के लिए चाहता हूं चाहे वे उनमें से कोई भी वापस आएं, और यह आसान है। उदाहरण के कोड के लिए, यह वर्तमान में क्या हो रहा है के सामान्य विचार है:हास्केल: मोनैडिक लेते समय?

Prelude> let f x = print x >> return x 
Prelude> mapM_ f [0..5] 
0 
1 
2 
3 
4 
5 
Prelude> 

मैं अपने वांछित साइड इफेक्ट मिलता है, और मैं परिणाम के बारे में परवाह नहीं है। लेकिन अब मुझे पहले आइटम के तुरंत बाद निष्पादन रोकना होगा जो मेरा वांछित परिणाम नहीं लौटाता है। - मान लीजिए 4 या उच्चतर की वापसी मूल्य को रोकने के लिए निष्पादन की आवश्यकता तो है कि मैं क्या चाहते करने के लिए यह है:

Prelude> takeWhile (<4) $ mapM f [0..5] 

कौन मुझे इस त्रुटि देता है:

 
<interactive>:1:22: 
    Couldn't match expected type `[b]' against inferred type `IO a' 
    In the first argument of `mapM', namely `f' 
    In the second argument of `($)', namely `mapM f ([0 .. 5])' 
    In the expression: takeWhile (< 4) $ mapM f ([0 .. 5])

और वह करने के लिए समझ में आता है मैं - परिणाम अभी भी आईओ monad में निहित है, और मैं सिर्फ आईओ monad में निहित दो मूल्यों की तुलना नहीं कर सकता। मुझे पता है कि यह मोनाड्स का उद्देश्य है - एक निश्चित स्थिति को पूरा करते समय एक साथ परिणाम चेनिंग और संचालन को छोड़ना - लेकिन इस मामले में आईओ मोनैड को "लपेटने" का एक आसान तरीका है, इस स्थिति में श्रृंखला को निष्पादित करना बंद करना MonadPlus का उदाहरण लिखने के बिना, मेरे चयन का?

क्या मैं टेक के समय के प्रयोजनों के लिए f से मूल्यों को "अनलिफ्ट" कर सकता हूं?

क्या यह एक समाधान है जहां मकान मालिक फिट बैठते हैं? फ़ैक्टर ने अभी तक मेरे साथ "क्लिक नहीं किया" है, लेकिन मुझे लगता है कि यह इंप्रेशन है कि यह उनका उपयोग करने के लिए एक अच्छी स्थिति हो सकती है।


अद्यतन:

@sth जो मैं चाहता के लिए निकटतम उत्तर है - वास्तव में, यह है कि लगभग वास्तव में क्या मैं के लिए जा रहा था, लेकिन मैं अभी भी है कि क्या वहाँ एक देखना चाहते हैं मानक समाधान जो स्पष्ट रूप से रिकर्सिव नहीं है - यह हास्केल है, आखिरकार! इस बात पर वापस देखकर कि मैंने अपने प्रश्न कैसे कहा, अब मैं देख सकता हूं कि मैं अपने वांछित व्यवहार के बारे में पर्याप्त स्पष्ट नहीं था।

f फ़ंक्शन मैंने उदाहरण के लिए ऊपर उपयोग किया था केवल एक उदाहरण था। वास्तविक कार्य सी में लिखे गए हैं और विशेष रूप से उनके दुष्प्रभावों के लिए उपयोग किए जाते हैं। मैं mapM_ f (takeWhile (<4) [0..5]) के @ टॉम के सुझाव का उपयोग नहीं कर सकता क्योंकि मुझे पता नहीं है कि निष्पादित होने तक कोई इनपुट वास्तव में सफलता या विफलता का परिणाम देगा या नहीं।

मुझे वास्तव में लौटाई गई सूची के बारे में परवाह नहीं है, या तो मैं सी कार्य को तब तक कॉल करना चाहता हूं जब तक सूची समाप्त न हो या पहले सी फ़ंक्शन विफलता कोड लौटाए।

सी शैली स्यूडोकोड में, मेरा व्यवहार होगा:

do { 
    result = function_with_side_effects(input_list[index++]); 
} while (result == success && index < max_index); 

तो फिर, @ sth का जवाब, सही व्यवहार है कि मैं चाहता हूँ करता है, सिवाय इसके कि परिणाम खारिज किया जा सकता (चाहिए?)। एक dropWhileM_ फ़ंक्शन मेरे उद्देश्यों के बराबर होगा। Control.Monad में ऐसा कोई फ़ंक्शन क्यों नहीं है या takeWhileM_ क्यों नहीं है? मैं देखता हूं कि a similar discussion on a mailing list था, लेकिन ऐसा लगता है कि इसमें से कुछ भी नहीं आया है।

+0

यह भी थोड़ा निराशाजनक है कि 'sortBy' को' sortByM :: Monad m => (a -> a -> m ऑर्डरिंग) के संदर्भ में परिभाषित नहीं किया गया है -> [a] -> m [a] '। –

उत्तर

15

आप m2 को रोकने के लिए एक अवसर, launchTheMissiles हो सकता है की जरूरत नहीं है sequence रूप

sequence xs = foldr (liftM2 (:)) (return []) xs 

liftM2 है कि आप देख रहे थे है के साथ समस्या यह परिभाषित कर सकता है!

liftM2 :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c 
liftM2 f m1 m2 = do 
    x1 <- m1 
    x2 <- m2 
    return (f x1 x2) 

निम्नलिखित में के रूप में guard का उपयोग करते हुए अपील लगता है:

sequenceUntil p xs = foldr (myLiftM2 p (:)) (return []) xs 
    where myLiftM2 p f m1 m2 = do 
      x1 <- m1 
      guard $ p x1 
      x2 <- m2 
      return (f x1 x2) 

कोड ऊपर अपने आवेदन में असफल हो जायेगी क्योंकि आईओ इकाई MonadPlus का एक उदाहरण नहीं है।

तो अपने हाथ थोड़ा और

module Main where 

import Control.Monad 

printx :: Int -> IO Int 
printx x = do 
    print x 
    return x 

sequenceUntil :: (Monad m) => (a -> Bool) -> [m a] -> m [a] 
sequenceUntil p xs = foldr (myLiftM2 (:) []) (return []) xs 
    where myLiftM2 f z m1 m2 = do 
      x1 <- m1 
      if p x1 then do x2 <- m2 
          return $ f x1 x2 
        else return z 

main :: IO() 
main = do 
    let as :: [IO Int] 
     as = map printx [1..10] 
    ys <- sequenceUntil (< 4) as 
    print ys 

हालांकि as 1 से 10 तक के ऊपर कार्रवाई की एक सूची है पकड़, उत्पादन

1 
2 
3 
4 
[1,2,3] 

परिणाम को छोड़ने से किया जाता है तो मामूली बात है:

sequenceUntil_ :: (Monad m) => (a -> Bool) -> [m a] -> m() 
sequenceUntil_ p xs = sequenceUntil p xs >> return() 

main :: IO() 
main = do 
    let as :: [IO Int] 
     as = map printx [1..] 
    sequenceUntil_ (< 4) as 

[1..] का उपयोग नोट करें जो नया कॉम दिखाता है बिनेटर maintains laziness


आप spanM पसंद कर सकते हैं:

spanM :: (Monad m) => (a -> Bool) -> [m a] -> m ([a], [m a]) 
spanM _ [] = return ([], []) 
spanM p (a:as) = do 
    x <- a 
    if p x then do (xs,bs) <- spanM p as 
       return (x:xs, bs) 
     else return ([x], as) 

ध्यान दें कि यह है कि यह परिणाम सूची में नाकाम रहने के तत्व शामिल हैं span से थोड़ी अलग है। जोड़ी का दूसरा शेष कार्य है। उदाहरण के लिए:

*Main> (xs,bs) <- spanM (< 4) as 
1 
2 
3 
4 
*Main> xs 
[1,2,3,4] 
*Main> sequence bs 
5 
6 
7 
8 
9 
10 
[5,6,7,8,9,10] 

फिर भी एक और विकल्प:

untilM :: Monad m => (a -> Bool) -> [m a] -> m() 
untilM p (x:xs) = do 
    y <- x 
    unless (p y) $ untilM p xs 

ध्यान दें कि विधेय की भावना पूरित है:

*Main> untilM (>= 4) as 
1 
2 
3 
4 
+0

+1, 'अनुक्रम के दौरान' की अच्छी परिभाषा। मैं पहले 'फ़ोल्डर' के बजाय 'foldM' का उपयोग करके इसे परिभाषित करने की कोशिश कर रहा था, लेकिन यह स्पष्ट रूप से काम नहीं करेगा, क्योंकि यह अभी भी पूरी सूची को मजबूर करता है। –

+0

किसी ने एक बेहतर नाम सुझाया: अनुक्रम। मैंने एक विकल्प के रूप में spanM भी जोड़ा। –

10

संपादित करें: अब मैं देख रहा हूं कि आप क्या देख रहे हैं।

gbacon ने एक अच्छा sequenceWhile फ़ंक्शन पोस्ट किया, जो आपको लगभग "आदिम" की आवश्यकता है।

असल में, क्योंकि आप केवल दुष्प्रभावों में रूचि रखते हैं, sequenceWhile_ पर्याप्त होना चाहिए। यहाँ एक परिभाषा (फिर से, gbacon से प्रेरित उसे वोट,!) है:

sequenceWhile_ :: (Monad m) => (a -> Bool) -> [m a] -> m() 
sequenceWhile_ p xs = foldr (\mx my -> mx >>= \x -> when (p x) my) 
          (return()) xs 

तुम इतनी तरह इस फोन:

Prelude Control.Monad> sequenceWhile (<4) $ map f [1..] 

मूल जवाब:

आप कर सकते हैं से मान के लिए takeWile के साथ उपयोग के लिए मोनाड को "अनलिफ्ट" करें, लेकिन आप takeWhile के उपयोग के लिए उपयोग कर सकते हैं इकाई!

liftM फ़ंक्शनफ़ंक्शन (m a -> m b) पर एक फ़ंक्शन लेगा, जहां m एक मोनाड है। liftM साथ

आप यह कर सकते हैं:

Prelude> :m + Control.Monad 
Prelude Control.Monad> let f x = print x >> return x 
Prelude Control.Monad> liftM (takeWhile (<4)) $ mapM f [0..5] 
0 
1 
2 
3 
4 
5 
[0,1,2,3] 
:

(Monad m => (a -> b) -> (m a -> m b) एक तरफ ध्यान दें के रूप में, आप इस तरह एक समारोह इस मामले में Hoogle पर अपने प्रकार के लिए खोज करके खोजकर पा सकते हैं)

अब, यह वही नहीं हो सकता जो आप चाहते थे। mapM एक सूची लौटने से पहले अनुक्रम में पूरी सूची में f फ़ंक्शन लागू करेगा। परिणामी सूची को takeWhile फ़ंक्शन को उठाया गया है।

यदि आप तीसरे तत्व के बाद मुद्रण करना बंद करना चाहते हैं, तो आपको प्रिंटिंग कॉल करना बंद करना होगा। इसका मतलब है, ऐसे तत्व के लिए f लागू न करें। तो, आप कुछ सरल की तरह अंत करेंगे:

Prelude> mapM_ f (takeWhile (<4) [0..5]) 

वैसे, आप आश्चर्य चाहिए क्योंmapM पहले, सब कुछ प्रिंट होगा सूची लौटने से पहले। आप उनकी परिभाषा के साथ काम करता है बदल कर भी इस देख सकते हैं:

mapM f [0..1] 
= 
sequence (map f [0..1]) 
= 
sequence (f 0 : map f [1..1]) 
= 
sequence (f 0 : f 1 : []) 
= 
sequence ((print 0 >> return 0) : f 1 : []) 
= 
sequence ((print 0 >> return 0) : (print 1 >> return 1) : []) 
= 
do x <- (print 0 >> return 0) 
    xs <- (sequence ((print 1 >> return 1) : [])) 
    return (x:xs) 
= 
do x <- (print 0 >> return 0) 
    xs <- (do y <- (print 1 >> return 1) 
      ys <- sequence ([]) 
      return (y:ys)) 
    return (x:xs) 
= 
do x <- (print 0 >> return 0) 
    xs <- (do y <- (print 1 >> return 1) 
      ys <- return [] 
      return (y:ys)) 
    return (x:xs) 
= 
do x <- (print 0 >> return 0) 
    xs <- (do y <- (print 1 >> return 1) 
      return (y:[])) 
    return (x:xs) 
= 
do x <- (print 0 >> return 0) 
    xs <- (print 1 >> return (1:[])) 
    return (x:xs) 
= 
do x <- (print 0 >> return 0) 
    print 1 
    return (x:1:[]) 
= 
do print 0 
    print 1 
    return (0:1:[]) 

उनकी परिभाषा के साथ कार्यों की जगह की यह प्रक्रिया संतुलन संबंधी तर्क कहा जाता है।

अगर मैं कोई गलती नहीं की, अब आप (उम्मीद) देख सकते हैं कि mapM (sequence का प्रयोग करके) पहले सब कुछ प्रिंट, और तो एक सूची देता है।

+0

वह वास्तविक व्यवहार नहीं था जिसे मैं चाहता था, लेकिन एक अच्छी व्याख्या के लिए +1। लिफ्टएम अब बहुत अधिक समझ में आता है। –

14

मुझे नहीं लगता कि मानक पुस्तकालय में एक takeWhileM ऐसा कुछ नहीं है, लेकिन आप इसे अपने आप को लिख सकता है ताकि केवल के रूप में ज्यादा आईओ रूप में की जरूरत निष्पादित होने

takeWhileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a] 
takeWhileM _ [] = return [] 
takeWhileM p (a:as) = 
    do v <- a 
     if p v 
     then do vs <- takeWhileM p as 
       return (v:vs) 
     else return [] 

आपूर्ति की सूची में केवल मूल्यांकन किया जाता है जब तक एक तत्व पाया जाता है, कि विधेय से मेल नहीं खाता:

*Main> takeWhileM (<4) (map f [1..5]) 
1 
2 
3 
4 
[1,2,3] 
+1

'Control.Monad.filterM' के साथ स्थिरता के लिए, मैं एक प्रकार की अपेक्षा करता हूं जैसे 'takeWhileM :: (मोनाड एम) => (ए -> एम बूल) -> [ए] -> एम [ए]'; इससे उपयोग में कुछ शामिल हो जाएगा। लिफ्टएम अनुक्रम। टेकवाइल्डएम (लिफ्टएम (<4)) '। लेकिन यह ओपी चाहता है कि यह क्या थोड़ा उलझन में है, तो मुझे। – ephemient

+0

@sth, @ephemient: मानक लाइब्रेरी में नहीं, लेकिन मेरे नए रिलीज "जनरेटर" पैकेज (हैकेज में) में एक और सामान्य ले लिया है जबकि समारोह। आपके टेक के दौरान, मोनैड के अंदर की कार्रवाइयों पर निर्भर न होने के लिए कार्यों की सूची की आवश्यकता होती है, जबकि जेनरेटर ले लेते हैं, जबकि एक सूची प्राप्त होती है जिसका मोनड एम होता है, वही और अधिक के लिए अनुमति देता है। – yairchu

+0

+1 जिस व्यवहार को मैं चाहता था उससे मेल खाने के लिए +1। मैंने सवाल अपडेट किया, और मैं प्रतीक्षा करने जा रहा हूं और देख सकता हूं कि किसी के पास मानक lib समाधान है या नहीं। यदि कोई नहीं है, तो मैं आपका जवाब स्वीकार कर रहा हूं। –

6

आप "List" पैकेज से एक का उपयोग कर सकते हैं।

import Control.Monad.ListT (ListT) 
import Data.List.Class (execute, fromList, joinM, takeWhile) 
import Prelude hiding (takeWhile) 

f x = print x >> return x 
main = 
    execute . takeWhile (< 4) . 
    joinM $ fmap f (fromList [0..5] :: ListT IO Int) 
  • fromList [0..5], 0..5 जो एक ListT IO (IO Int) जो अभी भी कोई monadic कार्य करता है में कोई monadic कार्यों
  • fmap f उस सूची परिणामों के प्रदर्शन से युक्त एक monadic सूची बनाता है, जैसा कि होता है।
  • joinM इसे ListT IO Int में बदल देता है। जब आइटम का उपभोग होता है तो प्रत्येक निहित क्रिया निष्पादित की जाएगी और इसका परिणाम सूची में मूल्य होगा।
  • takeWhile किसी भी List के लिए सामान्यीकृत है। [] और "Monad m => ListT m" दोनों List के उदाहरण हैं।
  • execute अपने सभी कार्यों को निष्पादित करने, monadic सूची का उपभोग करता है।
  • यदि आप परिणामों में रूचि रखते हैं तो आप "toList :: List m => m a -> ItemM m [a]" ("ItemM (ListT IO)" IO का उपयोग कर सकते हैं)। इसलिए इस मामले में यह "toList :: ListT IO a -> IO [a]" है। बेहतर अभी तक आप मोनैडिक सूची को निष्पादित करने के लिए scanl आदि जैसे उच्च-आदेश फ़ंक्शंस का उपयोग कर सकते हैं।
3

अभी हाल ही में आप MonadList hackage उपयोग कर सकते हैं इसमें handy functions शामिल है जैसे TakeWhileM, dropWhileM, deleteByM और कई अन्य।

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