Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jquery-ui/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell中的幺半群与Num_Haskell_Monoids - Fatal编程技术网

Haskell中的幺半群与Num

Haskell中的幺半群与Num,haskell,monoids,Haskell,Monoids,在过去的几个月里,我一直在学习Haskell,我遇到了一个令我困惑的幺半群的例子 鉴于这些定义: data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Read, Eq) instance F.Foldable Tree where foldMap f Empty = mempty foldMap f (Node x l r) = F.foldMap f l `mappend`

在过去的几个月里,我一直在学习Haskell,我遇到了一个令我困惑的幺半群的例子

鉴于这些定义:

data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Read, Eq) 

instance F.Foldable Tree where  
    foldMap f Empty = mempty  
    foldMap f (Node x l r) = F.foldMap f l `mappend`  
                             f x           `mappend`  
                             F.foldMap f r  
这棵树:

testTree = Node 5  
        (Node 3  
            (Node 1 Empty Empty)  
            (Node 6 Empty Empty)  
        )  
        (Node 9  
            (Node 8 Empty Empty)  
            (Node 10 Empty Empty)  
        )  
如果我跑步:

ghci> F.foldl (+) 0 testTree  
42  
ghci> F.foldl (*) 1 testTree  
64800  
当mappend折叠时,GHCi如何知道使用哪个幺半群?因为在默认情况下,树中的数字只是Num类型,我们从未明确表示它们是某些幺半群(如Sum或Product)的位置

那么,GHCi如何推断正确的幺半群来使用呢?还是我在这一点上完全错了


示例来源:

不需要
foldl
被翻译成
foldr
,它被翻译成
foldMap
而不是
Endo
,这意味着函数组合,意味着对您提供的函数进行简单的嵌套

或者别的什么。也就是说,
foldl
可以翻译成
foldMap
over
Dual。Endo
由左到右等组成

更新:是的

可折叠实例应满足以下规律:

foldr f z t = appEndo (foldMap (Endo . f) t ) z
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z  -- << --
fold = foldMap id
或等效值(此处为
(+)
案例所示)应用于用户提供的值——在您的案例中

                                                 0
另一件需要注意的事情是
Endo
Dual
newtype
s,因此所有这些机制都将由编译器完成,并在运行时消失。

对于
a->a
形式的普通函数,存在一个monoid实例(隐式,如果不是显式的话),其中,
mappend
对应于函数组合,
mempty
对应于
id
函数

什么是
(+)
?它是一个函数
(Num a)=>a->a->a
。如果你用
+
在你的可折叠的满是数字的地图上进行
折叠,你会把每一个数字都变成部分应用的
(+a
。瞧,你发现了神奇的
f
,它会把你可折叠的地图上的所有东西都变成一个幺半群

假设函数有一个直接幺半群实例,您可以执行以下操作:

foldMap (+) [1, 2, 3, 4]
,这将生成一个最终的
(Num a)=>a->a
,您可以将其应用于
0
以获得
10

但是,没有这样的直接实例,因此需要使用内置的
newtype
包装器
Endo
和相应的unwrapper
appEndo
,它们为
a->a
函数实现monoid

Prelude Data.Monoid> (appEndo (foldMap (Endo . (+)) [1, 2, 3, 4])) 0
10

这里的
Endo.
只是我们烦人的需要,我们需要提升普通的
a->a
s,这样它们就有了自然的
Monoid
实例。在
foldMap
完成后,通过将所有内容转换成
a->a
s并用合成链接在一起,我们使用提取最终的
a->a
ode>appEndo
,最后将其应用于
0
简短回答:它是
foldMap
签名中的类型约束

如果我们查看
Foldable
(更具体地说是
foldMap
)的源代码,我们会看到:

这意味着如果我们声明
Tree
Foldable
的一个成员(不是
Tree
有kind
*->*
),这意味着
foldMap
是在树上定义的,这样:
foldMap::monoidm=>(a->m)->treea->m
。因此这意味着结果类型(传递给
foldMap
)的函数的结果必须是
Monoid

Haskell是静态类型的:编译时之后,Haskell确切地知道每个函数实例中传递的类型。这意味着它知道输出类型是什么,以及如何处理它

现在
Int
不是
Monoid
的实例。您在这里使用
F.foldl(+)0 testTree
,这意味着您或多或少构造了一个“特殊”Monoid。如果我们看一下:

这有很多围绕参数
f
z
t
的逻辑。因此,让我们先将其分解

首先让我们看一下
Dual.Endo.flip f
。它是以下的缩写:

helper = \x -> Dual (Endo (\y -> f y x))
Dual
Endo
是每个构造函数都有一个参数的类型。因此,我们将
fyx
的结果包装在
Dual(Endo…
构造函数中

我们将使用它作为
foldMap
的函数。如果我们的
f
有类型
a->b->a
,那么这个函数有类型
b->Dual(Endo a)
。因此传递给
foldMap
的函数的输出类型有输出类型
Dual(Endo a)
。现在,如果我们检查源代码,我们会看到两件有趣的事情:

(请注意,它是
y`mappend`x
,而不是
x`mappend`y

因此,这里发生的是
foldMap
中使用的
mempty
mempty=Dual mempty=Dual(Endo id)
。因此,封装标识函数的
Dual(Endo…

此外,两个对偶的
mappend
归结为
Endo
中值的函数组合。因此:

mempty = Dual (Endo id)
mappend (Dual (Endo f)) (Dual (Endo g)) = Dual (Endo (g . f))
这意味着如果我们折叠树,如果我们看到一个
空的
(一片叶子),我们将返回
mempty
,如果我们看到一个
节点x l r
,我们将执行上面描述的
映射

-- specialized foldMap
foldMap f Empty = Dual (Endo id)  
foldMap f (Node x l r) =  Dual (Endo (c . b . a))
        where Dual (Endo a) = foldMap f l
              Dual (Endo b) = helper x
              Dual (Endo c) = foldMap f l
因此,对于每个
节点
我们在节点的子节点和项上从右到左进行函数组合。
a
c
也可以是树的组合(因为它们是递归调用)。对于
,我们什么也不做(我们返回
id
,但是
id
上的合成是不可操作的)

这意味着如果我们有一棵树:

5
|- 3
|  |- 1
|  `- 6
`- 9
   |- 8
   `- 10
这将产生一个函数:

(Dual (Endo ( (\x -> f x 10) .
              (\x -> f x 9) .
              (\x -> f x 8) .
              (\x -> f x 5) .
              (\x -> f x 6) .
              (\x -> f x 3) .
              (\x -> f x 1)
            )
      )
)
(省略了标识
mempty = Dual (Endo id)
mappend (Dual (Endo f)) (Dual (Endo g)) = Dual (Endo (g . f))
-- specialized foldMap
foldMap f Empty = Dual (Endo id)  
foldMap f (Node x l r) =  Dual (Endo (c . b . a))
        where Dual (Endo a) = foldMap f l
              Dual (Endo b) = helper x
              Dual (Endo c) = foldMap f l
5
|- 3
|  |- 1
|  `- 6
`- 9
   |- 8
   `- 10
(Dual (Endo ( (\x -> f x 10) .
              (\x -> f x 9) .
              (\x -> f x 8) .
              (\x -> f x 5) .
              (\x -> f x 6) .
              (\x -> f x 3) .
              (\x -> f x 1)
            )
      )
)
Endo ( (\x -> f x 10) .
       (\x -> f x 9) .
       (\x -> f x 8) .
       (\x -> f x 5) .
       (\x -> f x 6) .
       (\x -> f x 3) .
       (\x -> f x 1)
     )
( (\x -> f x 10) .
  (\x -> f x 9) .
  (\x -> f x 8) .
  (\x -> f x 5) .
  (\x -> f x 6) .
  (\x -> f x 3) .
  (\x -> f x 1)
)
f (f (f (f (f (f (f z 1) 3) 6) 5) 8) 9) 10