当前位置: 首页 > 知识库问答 >
问题:

Haskell中的Monoids和Num

裴彦
2023-03-14

在过去的几个月里,我一直在学习Haskell,我遇到了一个Monoid的例子,这让我感到困惑。

根据这些定义:

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  

示例来源:http://learnyouahaskell.com/functors-application-functors-and-monoids#monoids

共有1个答案

锺离旻
2023-03-14

简而言之:它是foldmap签名中的类型约束。

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

class Foldable (t :: * -> *) where
  ...
  foldMap :: Monoid m => (a -> m) -> t a -> m

这意味着,如果我们声明foldable的成员(并不是具有*->*),这意味着在该树上定义foldmap,例如:foldmap::Monoid m=>(a->m)->树a->m。因此,这意味着结果类型(以及传递给foldmap)M的函数结果)必须是monoid

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

现在int不是monoid的实例。这里使用f.foldl(+)0 testtree,这意味着您或多或少构造了一个“ad hoc”monoid。如果我们查看foldl的源代码,这种方法就会奏效:

foldl :: (b -> a -> b) -> b -> t a -> b
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z

这有很多围绕参数fzt的逻辑。所以让我们先分解一下。

让我们首先看一下dual。远藤。翻转f。这是:

helper = \x -> Dual (Endo (\y -> f y x))

dualendo都是类型,每个构造函数都接受一个参数。因此,我们将f y x的结果包装在dual(Endo…)构造函数中。

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

instance Monoid (Endo a) where
        mempty = Endo id
        Endo f `mappend` Endo g = Endo (f . g)

instance Monoid a => Monoid (Dual a) where
        mempty = Dual mempty
        Dual x `mappend` Dual y = Dual (y `mappend` x)

(请注意,它是Y`mappend`X,而不是X`mappend`Y)。

因此,这里发生的事情是,foldmap中使用的memptymempty=Dual mempty=Dual(Endo id)。因此,封装标识函数的dual(Endo...)

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

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)
            )
      )
)

(省略了身份,使它更干净)。这是getdual(foldMap(Dual.Endo.flip f))的结果。但是现在我们需要postprocess这个结果。使用getdual,我们获得包装在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)
     )
( (\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
 类似资料:
  • Haskell 的一些特色,像是纯粹性,高端函数,algebraic data types,typeclasses,这些让我们可以从更高的角度来看到 polymorphism 这件事。不像 OOP 当中需要从庞大的型态阶层来思考。我们只需要看看手边的型态的行为,将他们跟适当地 typeclass 对应起来就可以了。像 Int 的行为跟很多东西很像。好比说他可以比较相不相等,可以从大到小排列,也可以

  • 本文向大家介绍Scala和Haskell之间的区别,包括了Scala和Haskell之间的区别的使用技巧和注意事项,需要的朋友参考一下 Scala和Haskell都是最近开发的现代编程语言。但是两者都是不同类型的编程语言。同样,两者都是为了实现不同的目的而创建的。 在这里,我们将看到编程语言之间的区别点。但是首先,学习一些编程语言的基础知识。 Scala编程语言 Scala是基于Java构建的通用

  • 问题内容: 两者都是其类型是所有类型(无人居住)的交集的术语。两者都可以在代码中传递,而不会失败,直到尝试对其进行评估。我能看到的唯一区别是,在Java中,存在一个漏洞,可以对一个操作进行精确的评估,即引用相等比较(),而在Haskell 中,如果不抛出异常就无法进行评估。这是唯一的区别吗? 编辑 我真正想解决的问题是,为什么在Java 中包含如此明显的错误决定,Haskell如何逃避呢?在我看来

  • 到目前为止,我们讨论的所有示例本质上都是静态的。在本章中,我们将学习如何Haskell与用户动态交互。学习Haskell中使用的不同输入和输出技术。 1. 文件和流 到目前为止,我们已经对程序本身中的所有输入进行了硬编码,在前面学习的内容中都是从静态变量获取输入。本小节中,我们学习如何从外部文件读取和写入。 创建一个文件并命名为abc.txt。接下来,在此文本文件中输入以下一行: 接下来,我们将编

  • Haskell是一种函数语言,它是严格类型化的,Haskell编译器在编译时知道整个应用程序中使用的数据类型。 1. 内置类型类 在Haskell中,每个语句都被视为数学表达式,并且此表达式的类别称为类型()。可以说是在编译时使用的表达式的数据类型。 要了解有关类型的更多信息,可以使用命令。以通用的方式可以将类型视为值,而可以将类型类视为一组相似类型的类型。在本章中,我们将学习不同的内置类型。 2

  • 我必须写一个函数来求和一列数字的立方体。 这是我目前的代码: 问题是,当我运行它时,会出现以下错误: 没有因使用“it”而产生的(Num[t0])实例 在交互式GHCI命令的stmt中:打印它