【问题标题】:How do you implement monoid interface for this tree in haskell?你如何在haskell中为这棵树实现monoid接口?
【发布时间】:2013-02-24 18:03:36
【问题描述】:

请原谅术语,我的心还在弯曲。

树:

data Ftree a = Empty | Leaf a | Branch ( Ftree a ) ( Ftree a )
    deriving ( Show )

我有几个问题:

  1. 如果Ftree 不能是Empty,它是否不再是Monoid,因为没有身份值。

  2. 你将如何用这棵树实现mappend?可以随便随便把两棵树嫁接在一起吗?

  3. 对于二叉搜索树,您是否必须自省两棵树中的某些元素以确保 mappend 的结果仍然是 BST?

为了记录,Ftree 可以在这里做一些其他的事情:

instance Functor Ftree where
    fmap g Empty             = Empty
    fmap g ( Leaf a )        = Leaf ( g a )
    fmap g ( Branch tl tr )  = Branch ( fmap g tl ) ( fmap g tr )

instance Monad Ftree where 
    return             = Leaf
    Empty        >>= g = Empty
    Leaf a       >>= g = g a
    Branch lt rt >>= g = Branch ( lt >>= g ) ( rt >>= g )

【问题讨论】:

  • 我不明白这怎么可能是一个幺半群。 mappend 必须是关联的,如果你只是做类似mappend = Branch 的事情,那肯定不是。

标签: haskell functional-programming monoids


【解决方案1】:

您的问题有三个答案,一个是挑剔的,一个是无用的,一个是抽象的:

挑剔的答案

instance Monoid (Ftree a) where
    mempty = Empty
    mappend = Branch

这是Monoid 类型类的实例,但不满足任何必需的属性。

无益的答案

你想要什么 Monoid?仅仅在没有更多信息的情况下要求一个幺半群实例就像在没有给出问题的情况下要求一个解决方案。有时有一个自然的幺半群实例(例如列表)或只有一个实例(例如(),忽略定义的问题)。我认为两者都不是。

顺便说一句:如果你的树在内部节点上有数据递归组合两棵树,那将会是一个有趣的幺半群实例......

抽象答案

既然你给了一个Monad (Ftree a)实例,有一个通用的方法来获取一个Monoid实例:

instance (Monoid a, Monad f) => Monoid (f a) where
    mempty = return mempty
    mappend f g = f >>= (\x -> (mappend x) `fmap` g)

让我们检查一下这是否是一个 Monoid。我使用<> = mappend。我们假设Monad 法律成立(我没有检查你的定义)。此时,召回Monad laws written in do-notation

我们的mappend,用do-Notation写成:

mappend f g = do
  x <- f
  y <- g
  return (f <> g)

所以我们现在可以验证幺半群定律:

左身份

mappend mempty g
≡ -- Definition of mappend
do 
  x <- mempty
  y <- g
  return (x <> y)
≡ -- Definition of mempty
do 
  x <- return mempty
  y <- g
  return (x <> y)
≡ -- Monad law
do 
  y <- g
  return (mempty <> y)
≡ -- Underlying monoid laws
do 
  y <- g
  return y
≡ -- Monad law
g

正确的身份

mappend f mempty 
≡ -- Definition of mappend
do
  x <- f
  y <- mempty
  return (x <> y)
≡ -- Monad law
do
  x <- f
  return (x <> mempty)
≡ -- Underlying monoid laws
do 
  x <- f
  return x
≡ -- Monad law
f

最后是重要的结合律

mappend f (mappend g h)
≡ -- Definition of mappend
do
  x <- f
  y <- do
    x' <- g
    y' <- h
    return (x' <> y')
  return (x <> y)
≡ -- Monad law
do
  x <- f
  x' <- g
  y' <- h
  y <- return (x' <> y')
  return (x <> y)
≡ -- Monad law
do
  x <- f
  x' <- g
  y' <- h
  return (x <> (x' <> y'))
≡ -- Underlying monoid law
do
  x <- f
  x' <- g
  y' <- h
  return ((x <> x') <> y')
≡ -- Monad law
do
  x <- f
  x' <- g
  z <- return (x <> x')
  y' <- h
  return (z <> y')
≡ -- Monad law
do
  z <- do
    x <- f
    x' <- g
    return (x <> x')
  y' <- h
  return (z <> y')
≡ -- Definition of mappend
mappend (mappend f g) h

因此,对于每个(正确的)Monad(甚至对于每个应用函子,正如 Jake McArthur 在#haskell 上指出的那样),都有一个 Monoid 实例。它可能是也可能不是您正在寻找的那个。

【讨论】:

  • 如果树被定义为'data Tree a = Empty | 会有什么不同吗?叶一|节点 (树 a) a (树 a)' ?我感到困惑的原因是因为对于此处找到的 10.2 实例和示例:haskell.org/haskellwiki/Typeclassopedia#Definition_5,他们使用 mappend 为树实现了可折叠
  • 是的,那么我认为有一个实例:只需递归组合树,使用mzero 表示其中一棵树中不存在的任何值。
  • 哇,一般答案部分的额外内容真的很有帮助,非常感谢!
  • 实际上,我很想交换“无用”和“一般”答案的标签。另外,你真的是指到处都是mzero,还是应该写成mempty
  • 你对mempty 的看法当然是正确的,已修复。并将“一般”答案重命名为“抽象”。
猜你喜欢
  • 1970-01-01
  • 2019-10-20
  • 2012-09-21
  • 2021-06-08
  • 2010-09-24
  • 2014-11-01
  • 2020-02-05
  • 2010-09-16
  • 2013-09-19
相关资源
最近更新 更多