【问题标题】:Implement mapM without using sequence在不使用序列的情况下实现 mapM
【发布时间】:2016-04-20 11:21:13
【问题描述】:

我目前正在尝试解决 20 Intermediate Haskell Excercises 练习,但在不使用 sequence 的情况下尝试实施 mapM(练习中的 moppy)时遇到问题。

我只能通过简单地应用fmap 来生成[m b],但我不知道如何继续:

moppy :: [a] -> (a -> m b) -> m [b]
moppy la f = furry' f la -- how do I transform [m b] to m [b] without sequence

谁能给我提示一下看哪个方向?

【问题讨论】:

  • 一般来说,如果您想添加比map 允许的更多的“上下文”,请考虑使用折叠或显式递归来遍历您的数据结构。

标签: haskell monads


【解决方案1】:

正如 Benjamin Hodgson 所说,在现代,我们应该将Applicative 用于此特定目的:

myMapM :: Applicative f
       => (a -> f b) -> [a] -> f [b]

我们希望myMapM f [] 成为pure [](我们不可能得到任何bs!),所以

myMapM f [] = pure []

现在我们进入问题的核心,找出递归步骤。我们希望myMapM f (x : xs) 执行f x,执行myMapM f xs,并合并结果。所以

myMapM f [] = pure []
myMapM f (x : xs) = (:) <$> f x <*> myMapM f xs

当使用列表做一些好的和常规的事情时,我们通常可以只使用foldr

myMapM f = foldr go (pure []) where
  go x r = (:) <$> f x <*> r

【讨论】:

  • 为了真正的不可思议,myMapM = (`foldr` pure []) . (((&lt;*&gt;) . fmap (:)) .)
  • liftA2 (:) . ffoldr 中,得到一个单行,并避免恐慌括号。 :)
【解决方案2】:

好吧,如果没有太多剧透,我不知道该怎么做——所以在这里你可以使用一个非常基本/递归的定义:

moppy :: Monad m => [a] -> (a -> m b) -> m [b]
moppy [] _ = return []
moppy (x:xs) f = do
  y  <- f x
  ys <- moppy xs f
  return (y:ys)

它应该是不言自明的——请注意,您需要Monad m 约束(我认为您无论如何都想要它,因为没有它就变得相当不可能;))

【讨论】:

  • 你只需要Applicative mmapM(又名traverse)。 traverse f (x:xs) = (:) &lt;$&gt; f x &lt;*&gt; traverse f xs
  • @BenjaminHodgson 告诉你这是 基本的 - IMO 如果他们与 monads 斗争,告诉某人 Traversable 有点问题(我认为大多数人都可以处理 do 符号因为它们看起来很熟悉-势在必行)-但是您为什么不将其添加为一个不错的答案呢?
  • 我们可以让 Monadic do... Applicative do... Functorial do!或在另一个方向,LiftF... LiftA2... liftBind。是的,我认为do 符号可能是通往 monads 的入门药物,甚至应该比&gt;&gt;= 更早地出现,甚至提到“monad”这个名字。想想看,教Haskell应该从main = do { putStrLn "Hello, World!"; return () }开始!
【解决方案3】:

当您开始仅使用 &gt;&gt;=return 实现 mapM 时,它可能会有所帮助。你最终会得到类似的东西:

mapM' :: Monad m => (a -> m b) -> [a] -> m [b]
mapM' _ []     = return []
mapM' f (x:xs) =        f x           >>=
                 \y  -> mapM' f xs    >>=
                 \ys -> return (y:ys)

这种给你答案,就像之前的海报提到的那样。您需要做的就是更改参数的顺序:

moppy :: (Misty m) => [a] -> (a -> m b) -> m [b]      
moppy []     _ = unicorn []
moppy (x:xs) f = banana (\y -> banana (\ys -> unicorn (y:ys)) (moppy xs f)) (f x)

或者:

moppy :: (Misty m) => [a] -> (a -> m b) -> m [b]      
moppy []     _ = unicorn []
moppy (x:xs) f = (\y  -> (\ys -> unicorn (y:ys)) `banana` moppy xs f) `banana` f x

或者:

moppy :: (Misty m) => [a] -> (a -> m b) -> m [b]      
moppy []     _ = unicorn []
moppy (x:xs) f = let g y = let h ys = unicorn (y : ys)
                            in h `banana` moppy xs f
                  in g `banana` f x 

【讨论】:

    【解决方案4】:

    采取这个实现:

    moppy :: Monad m => (a -> m b) -> [a] -> m [b]
    moppy f xs = foldr g n xs 
      where
        n = return []
        g x mys = do {
          y  <- f x ;
          ys <- mys ;
          return (y:ys) }
    

    mys :: m [b] 因为foldr g n (x:xs) = g x (foldr g n xs)。)

    (改编自 C9 讲座:Ralf Lämmel - Going Bananas [8:06 min - youtube)。

    【讨论】:

      猜你喜欢
      • 2022-08-09
      • 1970-01-01
      • 2013-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-07
      • 2011-01-11
      • 2014-09-22
      相关资源
      最近更新 更多