所以,我现在正在学习Haskell,我想确认或揭穿我对monoid的理解。
我从阅读CIS194课程中得到的结论是,monoid基本上是" API"用于在自定义集上定义自定义二进制操作。
我比较多地告诉自己,我偶然发现了大量令人困惑的教程,试图澄清这个问题,所以我不再那么肯定了。
我有不错的数学背景,但我对所有的比喻感到困惑,并且寻找明确的是/否回答我对monoid的理解。
答案 0 :(得分:8)
在抽象代数中,数学的一个分支,monoid是一个代数结构,具有单个关联二元运算和一个恒等元素。
我认为你的理解是正确的。从编程的角度来看,Monoid是一个具有两种"方法的接口。必须实施。
您的描述中似乎唯一缺少的是"身份",没有它,您将描述Semigroup。
任何有"零"或者"空"并且组合两个值的方法可以是Monoid。需要注意的一点是,可以通过多种方式将一个集合/类型设置为Monoid,例如通过带有addition
的{{1}}或带有0
的{{1}}身份multiplication
。
答案 1 :(得分:5)
:
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
,我可以使用foldMap
和Sum
monoid来获取Ints的总和,或者使用Product
来获取产品等
最后,使用<>
可以提供方便。例如,有大量不同的Set实现,但对于所有这些实现s <> t
始终是两个集合s
和t
(相同类型)的并集。这使我能够编写与该集合的底层实现无关的代码,从而简化了我的代码。对于许多其他数据结构也可以这样说,例如,序列,树木,地图,优先级队列等。