我对monoid的理解是否有效?

时间:2015-09-02 17:17:22

标签: haskell operators monoids binary-operators

所以,我现在正在学习Haskell,我想确认或揭穿我对monoid的理解。

  

我从阅读CIS194课程中得到的结论是,monoid基本上是" API"用于在自定义集上定义自定义二进制操作。

我比较多地告诉自己,我偶然发现了大量令人困惑的教程,试图澄清这个问题,所以我不再那么肯定了。

我有不错的数学背景,但我对所有的比喻感到困惑,并且寻找明确的是/否回答我对monoid的理解。

3 个答案:

答案 0 :(得分:8)

From Wikipedia

  

在抽象代数中,数学的一个分支,monoid是一个代数结构,具有单个关联二元运算和一个恒等元素。

我认为你的理解是正确的。从编程的角度来看,Monoid是一个具有两种"方法的接口。必须实施。

您的描述中似乎唯一缺少的是"身份",没有它,您将描述Semigroup

任何有"零"或者"空"并且组合两个值的方法可以是Monoid。需要注意的一点是,可以通过多种方式将一个集合/类型设置为Monoid,例如通过带有addition的{​​{1}}或带有0的{​​{1}}身份multiplication

答案 1 :(得分:5)

来自Wolfram的

  

monoid是一个在关联二元运算下关闭的集合,在S中具有一个标识元素I,对于S中的所有a,Ia = aI = a。

来自Wiki:

  

在抽象代数中,数学的一个分支,monoid是一个代数结构,具有单个关联二元运算和一个恒等元素。

所以你的直觉或多或少是正确的。

您应该记住,没有为"自定义设置定义"在Haskell但是一种类型。区别很小(因为类型理论中的类型与集合论中的集合非常相似),但是您可以定义Monoid实例的类型不必是表示数学集的类型。

换句话说:类型描述了该类型的所有值的集合。 Monoid是一个"界面"声明声称遵守该接口的任何类型必须提供一个标识值,一个二进制操作组合该类型的两个值,并且这些方程应满足一些方程式,以便所有通用的Monoid操作按预期工作(例如幺半群值列表的一般求和)并且不会产生不合逻辑/不一致的结果。

另请注意,如果类型是Monoid类的实例,则需要在该集合(类型)中存在标识元素。

例如,自然数在两个加法项下形成一个Monoid(identity = 0):

0 + n = n
n + 0 = n

以及乘法(identity = 1):

1 * n = n
n * 1 = n

还列出了++(身份= [])下的幺半群:

[] ++ xs = xs
xs ++ [] = xs

同样,a -> a类型的函数在组合下形成一个monoid(identity = id

id . f = f
f . id = f

因此,重要的是要记住,Monoid不是关于表示集合的类型,而是关于作为集合查看的类型,可以这么说。

作为一个错误构造的Monoid实例的例子,请考虑:

import Data.Monoid

newtype MyInt = MyInt Int deriving Show

instance Monoid MyInt where
  mempty = MyInt 0
  mappend (MyInt a) (MyInt b) = MyInt (a * b)

如果您现在尝试mconcat MyInt值列表,那么您将始终获得MyInt 0因为身份值0和二进制操作*不能在一起玩得很好:

λ> mconcat [MyInt 1, MyInt 2]
MyInt 0

答案 2 :(得分:3)

在基本级别,你是对的 - 它只是我们用<>表示的二元运算符的API。

然而,monoid概念的价值在于它与其他类型和类的关系。在文化方面,我们认为<>是加入/追加两种相同类型的东西的自然方式。

考虑这个例子:

{-# LANGUAGE OverloadedStrings #-}

import Data.Monoid

greet x = "Hello, " <> x

函数greet极其多态 - x可以是String,ByteString或Text,仅举几个可能性。而且,在每种情况下,它基本上都是你所期望的 - 它将x附加到字符串`&#34; Hello,&#34;。

此外,有许多算法可用于任何可以累积的算法,这些算法适用于Monoid的推广。例如,考虑foldMap类中的Foldable函数:

foldMap ::  Monoid m => (a -> m) -> t a -> m

foldMap不仅概括了折叠结构的想法,而且我可以通过替换正确的Monoid实例来概括如何执行累积。

如果我有一个包含Ints的可折叠结构t,我可以使用foldMapSum monoid来获取Ints的总和,或者使用Product来获取产品等

最后,使用<>可以提供方便。例如,有大量不同的Set实现,但对于所有这些实现s <> t始终是两个集合st(相同类型)的并集。这使我能够编写与该集合的底层实现无关的代码,从而简化了我的代码。对于许多其他数据结构也可以这样说,例如,序列,树木,地图,优先级队列等。