我对幺半群的理解是否有效?
Is my understanding of monoid valid?
所以,我目前正在学习 Haskell,我想确认或揭穿我对幺半群的理解。
What I figured out from reading CIS194 course is that monoid is basically "API" for defining custom binary operation on custom set.
比起我去了解更多信息,我偶然发现了大量非常令人困惑的教程试图澄清这件事,所以我不再那么确定了。
我有不错的数学背景,但我只是对所有隐喻感到困惑,我正在寻找明确的yes/no答案来解释我对幺半群的理解。
In abstract algebra, a branch of mathematics, a monoid is an algebraic structure with a single associative binary operation and an identity element.
我认为你的理解是正确的。从编程的角度来看,Monoid 是一个接口,有两个必须实现的"methods"。
您的描述中似乎唯一缺少的部分是 "identity",没有它您描述的是 Semigroup。
任何具有 "zero" 或 "empty" 的东西以及组合两个值的方法都可以是 Monoid。需要注意的一件事是,set/type 可能以不止一种方式成为 Monoid,例如通过 addition
和身份 0
的数字,或 multiplication
身份 1
.
来自 Wolfram:
A monoid is a set that is closed under an associative binary operation and has an identity element I in S such that for all a in S, Ia=aI=a.
来自维基:
In abstract algebra, a branch of mathematics, a monoid is an algebraic structure with a single associative binary operation and an identity element.
所以你的直觉或多或少是正确的。
您只应记住,它不是为 Haskell 中的 "custom set" 定义的,而是一种类型。区别很小(因为类型论中的类型与集合论中的集合非常相似)但是您可以为其定义 Monoid 实例的类型不必是表示数学集合的类型。
换句话说:一个类型描述了属于该类型的所有值的集合。 Monoid 是一个 "interface" 声明任何声称遵守该接口的类型都必须提供一个标识值,一个结合该类型的两个值的二元运算,并且有一些方程式应该满足这些方程以便所有通用 Monoid操作按预期工作(例如幺半群值列表的一般求和)而不产生 illogical/inconsistent 结果。
另外,请注意,要使类型成为 Monoid 的实例,需要在该集合(类型)中存在标识元素 class。
例如自然数在两个加法下形成一个幺半群(identity = 0
):
0 + n = n
n + 0 = n
以及乘法(恒等式=1
):
1 * n = n
n * 1 = n
还列出 ++
下的一个幺半群(identity = []
):
[] ++ xs = xs
xs ++ [] = xs
此外,类型 a -> a
的函数在组合下形成一个幺半群 (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
在基本层面上你是对的 - 它只是一个 API 的二元运算符,我们用 <>
表示。
然而,幺半群概念的价值在于它与其他类型和 classes 的关系。从文化上讲,我们已经决定 <>
是 joining/appending 两个相同类型的东西放在一起的自然方式。
考虑这个例子:
{-# LANGUAGE OverloadedStrings #-}
import Data.Monoid
greet x = "Hello, " <> x
函数 greet
具有极高的多态性 - x
可以是字符串、字节字符串或文本,仅举几例。此外,在每种情况下,它基本上都按照您的预期进行 - 它将 x
附加到字符串 `"Hello, ".
此外,有很多算法可以处理任何可以累积的东西,这些都是泛化到 Monoid 的很好的候选者。例如,考虑来自 Foldable
class:
的 foldMap
函数
foldMap :: Monoid m => (a -> m) -> t a -> m
不仅 foldMap
概括了折叠结构的想法,而且我可以概括如何通过替换正确的 Monoid 实例来执行累加。
如果我有一个包含 Ints 的可折叠结构 t
,我可以使用 foldMap
和 Sum
幺半群来获得 Ints 的总和,或者使用 Product
获取产品等
最后,使用 <>
提供了便利。例如,有大量不同的 Set 实现,但对于所有这些实现,s <> t
始终是两个集合 s
和 t
(相同类型)的并集。这使我能够编写与集合的底层实现无关的代码,从而简化了我的代码。对于许多其他数据结构也可以这样说,例如序列、树、映射、优先队列等
所以,我目前正在学习 Haskell,我想确认或揭穿我对幺半群的理解。
What I figured out from reading CIS194 course is that monoid is basically "API" for defining custom binary operation on custom set.
比起我去了解更多信息,我偶然发现了大量非常令人困惑的教程试图澄清这件事,所以我不再那么确定了。
我有不错的数学背景,但我只是对所有隐喻感到困惑,我正在寻找明确的yes/no答案来解释我对幺半群的理解。
In abstract algebra, a branch of mathematics, a monoid is an algebraic structure with a single associative binary operation and an identity element.
我认为你的理解是正确的。从编程的角度来看,Monoid 是一个接口,有两个必须实现的"methods"。
您的描述中似乎唯一缺少的部分是 "identity",没有它您描述的是 Semigroup。
任何具有 "zero" 或 "empty" 的东西以及组合两个值的方法都可以是 Monoid。需要注意的一件事是,set/type 可能以不止一种方式成为 Monoid,例如通过 addition
和身份 0
的数字,或 multiplication
身份 1
.
来自 Wolfram:
A monoid is a set that is closed under an associative binary operation and has an identity element I in S such that for all a in S, Ia=aI=a.
来自维基:
In abstract algebra, a branch of mathematics, a monoid is an algebraic structure with a single associative binary operation and an identity element.
所以你的直觉或多或少是正确的。
您只应记住,它不是为 Haskell 中的 "custom set" 定义的,而是一种类型。区别很小(因为类型论中的类型与集合论中的集合非常相似)但是您可以为其定义 Monoid 实例的类型不必是表示数学集合的类型。
换句话说:一个类型描述了属于该类型的所有值的集合。 Monoid 是一个 "interface" 声明任何声称遵守该接口的类型都必须提供一个标识值,一个结合该类型的两个值的二元运算,并且有一些方程式应该满足这些方程以便所有通用 Monoid操作按预期工作(例如幺半群值列表的一般求和)而不产生 illogical/inconsistent 结果。
另外,请注意,要使类型成为 Monoid 的实例,需要在该集合(类型)中存在标识元素 class。
例如自然数在两个加法下形成一个幺半群(identity = 0
):
0 + n = n
n + 0 = n
以及乘法(恒等式=1
):
1 * n = n
n * 1 = n
还列出 ++
下的一个幺半群(identity = []
):
[] ++ xs = xs
xs ++ [] = xs
此外,类型 a -> a
的函数在组合下形成一个幺半群 (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
在基本层面上你是对的 - 它只是一个 API 的二元运算符,我们用 <>
表示。
然而,幺半群概念的价值在于它与其他类型和 classes 的关系。从文化上讲,我们已经决定 <>
是 joining/appending 两个相同类型的东西放在一起的自然方式。
考虑这个例子:
{-# LANGUAGE OverloadedStrings #-}
import Data.Monoid
greet x = "Hello, " <> x
函数 greet
具有极高的多态性 - x
可以是字符串、字节字符串或文本,仅举几例。此外,在每种情况下,它基本上都按照您的预期进行 - 它将 x
附加到字符串 `"Hello, ".
此外,有很多算法可以处理任何可以累积的东西,这些都是泛化到 Monoid 的很好的候选者。例如,考虑来自 Foldable
class:
foldMap
函数
foldMap :: Monoid m => (a -> m) -> t a -> m
不仅 foldMap
概括了折叠结构的想法,而且我可以概括如何通过替换正确的 Monoid 实例来执行累加。
如果我有一个包含 Ints 的可折叠结构 t
,我可以使用 foldMap
和 Sum
幺半群来获得 Ints 的总和,或者使用 Product
获取产品等
最后,使用 <>
提供了便利。例如,有大量不同的 Set 实现,但对于所有这些实现,s <> t
始终是两个集合 s
和 t
(相同类型)的并集。这使我能够编写与集合的底层实现无关的代码,从而简化了我的代码。对于许多其他数据结构也可以这样说,例如序列、树、映射、优先队列等