gpt4 book ai didi

haskell - `Data.Monoid` 中所有这些新型包装器的实用值(value)是什么?

转载 作者:行者123 更新时间:2023-12-03 14:30:25 24 4
gpt4 key购买 nike

在查看 Data.Monoid 时,我看到有各种newtype包装器,例如 All , Sum , 或 Product ,它编码各种幺半群。但是,当尝试使用这些包装器时,我不禁想知道使用它们的非 Data.Monoid 有什么好处。同行。比如比较比较繁琐的求和

print $ getSum $ mconcat [ Sum 33, Sum 2, Sum 55 ]

与更简洁的惯用变体相比
print $ sum [ 33, 2, 55 ]

但有什么意义呢?所有这些 newtype有什么实用值(value)吗? wrapper ? Monoid 有没有更有说服力的例子? newtype包装器的使用比上面的吗?

最佳答案

Monoid newtypes:一个零空间无操作告诉编译器该做什么

Monoids 非常适合将现有数据类型包装在新类型中,以告诉编译器您要执行什么操作。

由于它们是新类型,因此它们不会占用任何额外空间并应用 SumgetSum是无操作的。

示例:Foldable 中的 Monoids

泛化 foldr 的方法不止一种(参见 this very good question 了解最通用的折叠,如果您喜欢下面的树示例但想查看最通用的树折叠,请参见 this question)。

一种有用的方法(不是最通用的方法,但绝对有用)是说某些东西是可折叠的,如果您可以将其元素与二进制操作和开始/身份元素组合成一个。这就是 Foldable 的意义所在。类型类。
Foldable 不是显式传入二元运算和起始元素只是要求元素数据类型是 Monoid 的一个实例。

乍一看这似乎令人沮丧,因为我们只能对每种数据类型使用一个二进制操作 - 但我们应该使用 (+)0对于 Int取总和但从不取积,还是反过来?也许我们应该使用((+),0)对于 Int(*),1对于 Integer并在我们想要其他操作时进行转换?这不会浪费很多宝贵的处理器周期吗?

Monoids 救援

我们需要做的就是标记Sum如果要添加,请使用 Product 标记如果我们想要乘法,或者如果我们想要做一些不同的事情,甚至用手动新类型标记。

让我们折一些树!我们需要

fold :: (Foldable t, Monoid m) => t m -> m    
-- if the element type is already a monoid
foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
-- if you need to map a function onto the elements first
DeriveFunctorDeriveFoldable如果您想映射并折叠您自己的 ADT 而无需自己编写繁琐的实例,则扩展 ( {-# LANGUAGE DeriveFunctor, DeriveFoldable #-}) 非常有用。
import Data.Monoid
import Data.Foldable
import Data.Tree
import Data.Tree.Pretty -- from the pretty-tree package

see :: Show a => Tree a -> IO ()
see = putStrLn.drawVerticalTree.fmap show

numTree :: Num a => Tree a
numTree = Node 3 [Node 2 [],Node 5 [Node 2 [],Node 1 []],Node 10 []]

familyTree = Node " Grandmama " [Node " Uncle Fester " [Node " Cousin It " []],
Node " Gomez - Morticia " [Node " Wednesday " [],
Node " Pugsley " []]]

示例用法

字符串已经是一个使用 (++) 的幺半群和 [] ,所以我们可以 fold有它们,但数字不是,所以我们将使用 foldMap 标记它们.
ghci> see familyTree
" Grandmama "
|
----------------------
/ \
" Uncle Fester " " Gomez - Morticia "
| |
" Cousin It " -------------
/ \
" Wednesday " " Pugsley "
ghci> fold familyTree
" Grandmama Uncle Fester Cousin It Gomez - Morticia Wednesday Pugsley "
ghci> see numTree
3
|
--------
/ | \
2 5 10
|
--
/ \
2 1

ghci> getSum $ foldMap Sum numTree
23
ghci> getProduct $ foldMap Product numTree
600
ghci> getAll $ foldMap (All.(<= 10)) numTree
True
ghci> getAny $ foldMap (Any.(> 50)) numTree
False

滚动你自己的 Monoid

但是如果我们想找到最大元素呢?我们可以定义自己的幺半群。我不知道为什么 Max (和 Min )不在。也许是因为没有人喜欢考虑 Int是有界的,或者他们只是不喜欢基于实现细节的标识元素。无论如何,这里是:
newtype Max a = Max {getMax :: a}

instance (Ord a,Bounded a) => Monoid (Max a) where
mempty = Max minBound
mappend (Max a) (Max b) = Max $ if a >= b then a else b
ghci> getMax $ foldMap Max numTree :: Int  -- Int to get Bounded instance
10

结论

我们可以使用 newtype Monoid 包装器来告诉编译器以哪种方式将事物成对组合。

标签什么都不做,但显示要使用的组合功能。

这就像将函数作为隐式参数而不是显式参数传递(因为这就是类型类所做的事情)。

关于haskell - `Data.Monoid` 中所有这些新型包装器的实用值(value)是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22080564/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com