【问题标题】:Merging/Appending Justs in Haskell在 Haskell 中合并/追加 Justs
【发布时间】:2012-02-18 00:14:42
【问题描述】:

我正在尝试做在 Haskell 中必须非常明显的事情,即从 Just [1]Just [2]Just [1, 2]。但是我在网上找不到任何东西,因为我一直在寻找相关但无用的页面。那么,您是如何实现这一目标的呢?

【问题讨论】:

    标签: haskell monads applicative maybe


    【解决方案1】:

    你可以使用liftA2 (++):

    liftA2 (++) :: Maybe [a] -> Maybe [a] -> Maybe [a]
    

    liftA2 只是将二进制函数提升为ApplicativeApplicatives 是为在上下文中提升任意参数的函数而设计的,因此它们非常适合这一点。在这种情况下,我们使用的ApplicativeMaybe。要了解它是如何工作的,我们可以查看定义:

    liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
    liftA2 f a b = f <$> a <*> b
    

    (&lt;$&gt;) 只是将纯值上的任何函数提升为在f 内部运行的函数:(a -&gt; b) -&gt; f a -&gt; f b。 (这只是fmap 的别名,如果您熟悉Functors。)对于Maybe

    _ <$> Nothing = Nothing
    f <$> Just x = Just (f x)
    

    (&lt;*&gt;) 有点棘手:它将f 中的函数应用于f 中的值:f (a -&gt; b) -&gt; f a -&gt; f b。对于Maybe

    Just f <*> Just x = Just (f x)
    _ <*> _ = Nothing
    

    (其实f &lt;$&gt; xpure f &lt;*&gt; x是一回事,也就是Just f &lt;*&gt; x代表Maybe。)

    所以,我们可以扩展liftA2 (++)的定义:

    liftA2 (++) a b = (++) <$> a <*> b
    
    -- expand (<$>)
    liftA2 (++) (Just xs) b = Just (xs ++) <*> b
    liftA2 (++) _ _ = Nothing
    
    -- expand (<*>)
    liftA2 (++) (Just xs) (Just ys) = Just (xs ++ ys)
    liftA2 (++) _ _ = Nothing
    

    事实上,我们可以使用这些运算符将任何个参数的函数提升为任何Applicative,只需遵循liftA2的模式即可。这称为应用风格,在惯用的 Haskell 代码中很常见。在这种情况下,如果ab 已经是变量,那么直接通过编写(++) &lt;$&gt; a &lt;*&gt; b 来使用它可能会更加地道。 (另一方面,如果您部分应用它——例如,将它传递给高阶函数——那么liftA2 (++) 更可取。)

    每个Monad 都是Applicative,所以如果你发现自己试图将一个函数“提升”到上下文中,Applicative 可能就是你要找的。​​p>

    【讨论】:

    • 太棒了 :) 谢谢,你救了我扯头发。不要以为您知道 [2]Just [3] -> Just [2, 3] 的等价物吗? :)
    • @DeanBarnes: (2 :) &lt;$&gt; Just [3]
    • 很棒的答案,谢谢@ehird!从现在开始,这基本上是我的参考:)
    • 不客气! :) 概括@JoeyAdams 所说的内容,(xs ++) &lt;$&gt; mys 可用于在mys :: Maybe [a] 中添加任何xs :: [a]
    【解决方案2】:

    虽然@ehird 的回答很棒,但我会以如下形式使用一个笨拙的解决方案:

    mergeJust a b = do
        a' <- a
        b' <- b
        return (a' ++ b')
    

    【讨论】:

    • +1 即使是菜鸟,配备简单的工具,也可以解决这个问题。你也可以像 monad 理解一样写:[a' ++ b' | a' &lt;- a, b' &lt;- b]
    【解决方案3】:

    要将解决方案扩展到Justs 列表,您可以使用

    fmap join $ sequence [Just[1],Just[2],Just[3]]
    -- Just [1,2,3]
    

    【讨论】:

      【解决方案4】:

      由于其他解决方案中没有提到,我就在这里说一下。在我看来,完成任务的最简单方法是使用来自Data.Monoid&lt;&gt;(或mappend)。

      import Data.Monoid
      
      Just [1,2] <> Just [7,8] == Just [1,2,7,8]
      

      但是,请注意,与 ehird 的应用解决方案不同,此解决方案不会短路 Nothing 值。

      Just [1,2] <> Nothing ---> Just [1,2]
      --However
      (++) <$> Just [1,2] <*> Nothing ---> Nothing
      

      【讨论】:

      • 有时这是正确的行为,有时不是。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-31
      • 1970-01-01
      • 2014-08-29
      • 2012-07-05
      • 1970-01-01
      • 2011-01-09
      • 2011-07-08
      相关资源
      最近更新 更多