簡體   English   中英

如何將包含在monad中的列表中的元素取出來

[英]How to takeWhile elements in a list wrapped in a monad

有點謎題我想知道你是否可以幫我澄清一下。

讓我們定義一個返回列表的函數:

let f = replicate 3

我們想要做的是將此函數映射到無限列表,連接結果,然后只獲取與謂詞匹配的內容。

takeWhile (< 3) $ concatMap f [1..]

大! 返回[1,1,1,2,2,2] ,這就是我想要的。

現在,我想做類似的事情,但函數f現在將其結果包裝在Monad中。 在我的用例中,這是IO monad,但這適用於討論我的問題:

let f' x = Just $ replicate 3 x

要映射和連接,我可以使用:

fmap concat $ mapM f' [1..5]

返回: Just [1,1,1,2,2,2,3,3,3,4,4,4,5,5,5]

如果我想使用takeWhile ,這仍然有效:

fmap (takeWhile (< 3) . concat) $ mapM f' [1..5]

返回:只[1,1,1,2,2,2]。 大!

但是,如果我列出了無限列表的列表,那么這不符合我的預期:

fmap (takeWhile (< 3) . concat) $ mapM f' [1..]

看起來像takeWhile永遠不會發生。 不知何故,我沒有得到我期待的懶惰計算。 我有點迷茫。

問題不在於fmap + takeWhile不適用於包含在monad中的無限列表。 問題是mapM無法生成無限列表(至少不在Maybe monad中)。

想一想:如果f'為列表中的任何項返回Nothing ,則mapM必須返回Nothing 但是, mapM無法知道是否會在列表中的所有項目上調用f'發生這種情況。 所以它需要在知道結果是Nothing還是Just之前遍歷整個列表。 顯然這是無限列表的問題。

這應該做的伎倆:

takeWhileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
takeWhileM p [] = return []
takeWhileM p (m:ms) = do 
    x <- m
    if p x
      then liftM (x:) (takeWhileM p ms) 
      else return []

請參閱sepp2k的答案,解釋為什么你會失去懶惰。 例如,Identity monad或非列表monad不會出現此問題。

你無法映射出無限的Maybes列表。 mapM是map,后跟sequence。 這是序列的定義:

sequence ms = foldr k (return []) ms
  where
    k m m' = do { x <- m; xs <- m'; return (x:xs) }

由此我們看到序列評估列表中的每個monadic值。 由於它是無限列表,因此該操作不會終止。

編輯:

luqui和Carl提出了一個很好的觀點,即這並不適用於任何monad。 要了解它為什么不適用於Maybe,我們需要查看(>> =)的實現:

(>>=) m k = case m of
    Just x  -> k x
    Nothing -> Nothing

這里重點是我們在m上做一個案例。 這使得m嚴格,因為我們必須對其進行評估以確定如何繼續執行。 請注意,我們在這里沒有封裝x,所以它仍然是懶惰的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM