monoid同态到底是什么?

时间:2019-05-05 14:53:28

标签: scala haskell functional-programming category-theory monoids

我已经从{{3}}阅读到有关monoid同构的信息,并且无法理解100%。

作者说(强调原文):

  

length函数从String映射到Int ,同时保留   单体结构。这样的功能,可以将一个半身像映射到   另一种以这种保存方式被称为 monoid同态。在   一般而言,对于mono MN,同构f: M => N和所有值   x:My:M,以下等式成立:

f(x |+| y) == (f(x) |+| f(y))

f(mzero[M]) == mzero[N]

他的意思是说,由于数据类型StringInt是类半群,而函数length映射String => Int保留了类半群结构(Int是一个monoid),它被称为monoid同态,对吧?

4 个答案:

答案 0 :(得分:68)

  

他的意思是说,数据类型String和Int是等宽的。

StringInt都不是类半体动物。一个单面体是一个三元组(S,⊕,e),其中⊕是一个二元运算符⊕:S×S→S ,因此对于所有元素 a,b,c∈S认为(a⊕b)⊕c=a⊕(b⊕c)e∈S是一个“身份元素”,例如a⊕e=e⊕a= a StringInt是类型,因此基本上是一组值,但不是三元组。

文章说:

  

让我们将 String串联 Int添加作为   有关系的示例 monoids

因此,作者显然还提到了二元运算符(在(++)的情况下为String,在(+)的情况下为Int)。身份(String为空字符串,0Int)是隐式的;在非正式英语语篇中,将身份留给读者练习是很常见的。

现在假设我们有两个单半体结构(M,⊕,e m (N,⊗,e n ,然后将函数 f:M→N (如length称为monoid homomorphism [wiki],因为它认为 f(m 1 ⊕m 2 )= f(m 1 )⊗f(m 2 m 1 ,m 2 ∈M并且该映射还保留了标识元素: f(e m )= e n

例如length :: String -> Int是一个单面体同态,因为我们可以考虑String(++)""Int(+)0。它认为:

  1. length (s1 ++ s2) == length s1 + length s2(对于所有Strings1s2);和
  2. length "" == 0

答案 1 :(得分:18)

数据类型本身不能是monoid。对于Monoid,您需要数据类型T和另外两件事:

  • 一个关联二进制运算,我们称之为|+|,它接受​​两个T类型的元素并产生一个T类型的元素
  • 类型为T identity元素,我们将其称为i,这样对于类型为t的每个元素T,以下内容均成立:t |+| i = i |+| t = t

以下是monoid的一些示例:

  • 一组整数,其运算为加法且标识为零
  • 一组整数,其中运算=乘法,同一性= one
  • 操作=附加且标识=空列表的列表集
  • 一组字符串,其操作=串联,身份=空字符串

Monoid同态

通过将.length应用于字符串的所有元素,可以将字符串串联Monoid转换为整数加法Monoid。这两个集合都构成一个半体。顺便说一句,请记住,我们不能只说“一组整数构成一个单半体”。我们必须选择一个关联操作和一个相应的标识元素。如果我们采取除法运算,我们打破了第一条规则(而不是生成整数类型的元素,而可能生成float / double类型的元素)。

方法length允许我们从一个Monoid(字符串串联)转到另一个Monoid(整数加法)。如果这样的操作也保留了类半体结构,则它被认为是 monoid同态

保留结构意味着:

length(t1 |+| t2) = length(t1) |+| length(t2)

and

length(i) = i'

其中t1t2代表“源” monoid的元素,i是“源” monoid的标识,i'是“目的地”类半身像。您可以自己尝试一下,看看length确实是一个对字符串串联Monoid进行结构保留的操作,例如indexOf("a")不是。

Monoid同构

如所示,length将所有字​​符串映射到它们相应的整数,并形成一个以加法运算为运算符和以零表示为标识的monoid。但是我们不能返回-对于每个字符串,我们都可以计算出它的长度,但是在给定长度的情况下,我们无法重构“原始”字符串。如果可以的话,“向前”操作与“向后”操作相结合将形成 monoid同构

同构意味着能够来回移动而不会丢失任何信息。例如,如前所述,列表在附加为操作和空列表作为标识元素的情况下形成一个monoid。我们可以从“附加列表下的” monoid转到“附加矢量下的” monoid,然后返回而不会丢失任何信息,这意味着操作.toVector.toList共同形成同构。鲁纳尔在课文中提到的同构的另一个示例是StringList[Char]

答案 2 :(得分:2)

通俗地说,同构是保留结构的函数。在length函数的示例中,保留的结构是to字符串的长度之和等于相同字符串的串联长度。由于字符串和整数都可以视为等分体(当配备了等式和遵循二等性律的关联二进制运算时),length被称为类同态。

另请参阅其他答案以获取更多技术说明。

答案 3 :(得分:0)

trait Monoid[T] {
def op(a: T, b: T): T
def zero: T
}

val strMonoid = new Monoid[String] {
  def op(a: String, b: String): String = a ++ b
  def zero: String = ""
}

val lcMonoid = new Monoid[List[Char]] {
  def op(a: List[Char], b: List[Char]): List[Char] = a ::: b
  def zero = List.empty[Char]
}

通过函数f的同态

f{M.op(x,y)} = N.op(f(x),g(y))

for example, using toList available on String

//in REPL
scala> strMonoid.op("abc","def").toList == lcMonoid.op("abc".toList,"def".toList)
res4: Boolean = true
通过函数f和g

同构

M和N之间存在双向同构,

f{M.op(x,y)} = N.op(f(x),f(y))
g{N.op(x,y)} = M.op(g(x),g(y))

如果(f andThen g)和(g andThen f)都是识别函数,则通过f和g的等式M和N是同构的。

g{f{M.op(x,y)}} = g{N.op(f(x),f(y))} = M.op(g(f(x)),g(f(y))) = M.op(x,y)

例如,使用toList上可用的StringtoString上可用的List[Char](其中toList andThen toStringtoString andThen toList是身份函数)< / p>

scala> ( strMonoid.op("abc","def").toList ).toString == ( lcMonoid.op("abc".toList,"def".toList) ).toString
res7: Boolean = true