使用通用元组函数一次进行多次折叠

2024-05-06

如何编写一个接受类型函数元组的函数ai -> b -> ai并返回一个函数,该函数接受类型元素的元组ai, 类型的一个元素b,并将每个元素组合成一个新的元组ai:

那是签名应该是这样的

f :: (a1 -> b -> a1, a2 -> b -> a2, ... , an -> b -> an) -> 
     (a1, a2, ... , an) -> 
     b -> 
     (a1, a2, ... , an)

这样:

f (min, max, (+), (*)) (1,2,3,4) 5 = (1, 5, 8, 20) 

这样做的目的是让我可以写:

foldlMult' t = foldl' (f t)

然后做类似的事情:

foldlMult' (min, max, (+), (*)) (head x, head x, 0, 0) x 

一次完成多次折叠。 GHC 扩展没问题。


如果我理解你的例子正确的话,类型是ai -> b -> ai, not ai -> b -> a正如你第一次写的那样。让我们将类型重写为a -> ri -> ri,只是因为它帮助我思考。

首先要观察的是以下对应关系:

(a -> r1 -> r1, ..., a -> rn -> rn) ~ a -> (r1 -> r1, ..., rn -> rn)

这允许您编写这两个相反的函数:

pullArg :: (a -> r1 -> r1, a -> r2 -> r2) -> a -> (r1 -> r1, r2 -> r2)
pullArg (f, g) = \a -> (f a, g a)

pushArg :: (a -> (r1 -> r1, r2 -> r2)) -> (a -> r1 -> r1, a -> r2 -> r2) 
pushArg f = (\a -> fst (f a), \a -> snd (f a))

第二个观察:表格的类型ri -> ri有时被称为自同态,并且每个类型都有一个幺半群,以组合作为结合运算,以恒等函数作为恒等。这Data.Monoid包中有这样的包装:

newtype Endo a = Endo { appEndo :: a -> a }

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

这允许您重写之前的pullArg对此:

pullArg :: (a -> r1 -> r1, a -> r2 -> r2) -> a -> (Endo r1, Endo r2)
pullArg (f, g) = \a -> (Endo $ f a, Endo $ g a)

第三个观察:两个幺半群的乘积也是一个幺半群,根据这个例子也来自Data.Monoid:

instance (Monoid a, Monoid b) => Monoid (a, b) where
    mempty = (mempty, mempty)
    (a, b) `mappend` (c, d) = (a `mappend` c, b `mappend d)

对于任意数量参数的元组也是如此。

第四个观察:褶皱是由什么制成的? http://web.jaguarpaw.co.uk/~tom/blog/posts/2012-11-04-what-is-foldr-made-of.html回答:折叠是由幺半群组成的! http://byorgey.wordpress.com/2012/11/05/foldr-is-made-of-monoids/

import Data.Monoid

fold :: Monoid m => (a -> m) -> [a] -> m
fold f = mconcat . map f

This fold只是一个专业化foldMap from Data.Foldable,所以实际上我们不需要定义它,我们可以导入它更通用的版本:

foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m

If you fold with Endo,这与从右侧折叠相同。要从左侧折叠,您需要用Dual (Endo r) monoid:

myfoldl :: (a -> Dual (Endo r)) -> r -> -> [a] -> r
myfoldl f z xs = appEndo (getDual (fold f xs)) z


-- From `Data.Monoid`.  This just flips the order of `mappend`.
newtype Dual m = Dual { getDual :: m }

instance Monoid m => Monoid (Dual m) where
    mempty = Dual mempty
    Dual a `mappend` Dual b = Dual $ b `mappend` a

记住我们的pullArg功能?让我们稍微修改一下,因为您是从左侧折叠的:

pullArg :: (a -> r1 -> r1, a -> r2 -> r2) -> a -> Dual (Endo r1, Endo r2)
pullArg (f, g) = \a -> Dual (Endo $ f a, Endo $ g a)

我声称,这是你的 2 元组版本f,或者至少与其同构。您可以将折叠函数重构为以下形式a -> Endo ri,然后执行:

let (f'1, ..., f'n) = foldMap (pullArgn f1 ... fn) xs
in (f'1 z1, ..., f'n zn) 

还值得一看:可组合的流式折叠 http://www.haskellforall.com/2013/08/composable-streaming-folds.html,这是对这些想法的进一步阐述。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用通用元组函数一次进行多次折叠 的相关文章

  • 访问函数中的环境

    In main我可以读取我的配置文件 并将其提供为runReader somefunc myEnv正好 但somefunc不需要访问myEnv读者提供 链中的下一对也没有提供 需要 myEnv 中某些内容的函数是一个微小的叶函数 如何在不将
  • 为什么 Parsec 的 sepBy 停止并且不解析所有元素?

    我正在尝试解析一些逗号分隔的字符串 该字符串可能包含也可能不包含具有图像尺寸的字符串 例如 hello world 300x300 good bye world 我写了下面的小程序 import Text Parsec import qua
  • Haskell 中的实例声明

    我有这两个功能 primes sieve 2 where sieve p xs p sieve x x lt xs x mod p gt 0 isPrime number number 1 null x x lt takeWhile x g
  • 为什么 Haskell 的默认字符串实现是一个字符链接列表?

    Haskell 默认值的事实String众所周知 实现在速度和内存方面都效率不高 据我所知 lists一般来说 在 Haskell 中实现为单链表 并且适用于大多数小型 简单数据类型 例如Int 这似乎不是一个好主意 但是对于String这
  • 我应该在 Turtle 或 Foldl 包中使用折叠吗?

    我在使用 Turtle 时遇到了一些困难 直到盯着难以理解的错误消息几分钟后才意识到我使用了错误的fold功能 https hackage haskell org package turtle 1 5 8 docs Turtle Shell
  • Haskell:无法预期类型“Integer”与实际类型“Int”

    我已经盯着这段代码有一段时间了 但我无法理解该错误消息 divisors Integer gt Integer divisors n t t lt 1 n mod n t 0 length a gt Integer length 0 len
  • 纯函数怎么能做IO呢?

    我最近了解到莫纳德随机数 http hackage haskell org package MonadRandom 0 1 13 docs Control Monad Random Class html t 3aMonadRandom图书馆
  • 搜索重写规则

    有什么办法可以浏览或搜索重写规则吗 当我使用像这样的标志时 ddump rule firings or ddump rule rewrites我只是得到了触发的规则的名称以及它引起的重写 但没有得到实际的规则本身 理想情况下 我想通过 GH
  • 将两个 Int 值相除以获得 Float 的正确方法是什么?

    我想分两份IntHaskell 中的值并获得结果Float 我尝试这样做 foo Int gt Int gt Float foo a b fromRational a b 但 GHC 版本 6 12 1 告诉我 无法将预期类型 Intege
  • Haskell 中的分类结构

    Hask通常被认为是一个范畴 其对象是类型 态射是函数 然而 我看到 Conor McBride pigworker 警告不要使用Hask多次 1 https stackoverflow com a 45905082 474311 2 ht
  • 规范化且不可变的数据模型

    Haskell如何解决 规范化不可变数据结构 问题 例如 让我们考虑一个表示前女友 男友的数据结构 data Man Man name String exes Woman data Woman Woman name String exes
  • 以下两个 lambda 函数的空间复杂度

    我正在阅读以下内容 https en wikibooks org wiki Haskell Graph reduction https en wikibooks org wiki Haskell Graph reduction 其内容如下
  • Haskell 泛化问题(涉及列表理解)

    假设我想知道a上的所有要点 x y 矩形内的平面has 我可以使用列表推导式来计算 如下所示 let myFun2D x y x lt 0 2 y lt 0 2 现在 如果我想为一个人完成同样的事情 x y z 空间 我可以采取同样的方式并
  • Haskell 中列表列表的笛卡尔积

    给定一个长度列表的列表x所有子列表的长度都相同y 输出y x长度列表x包含每个子列表中的一项 例子 x 3 y 2 1 2 3 4 5 6 Output 2 3 8不同的输出 1 3 5 1 4 5 1 3 6 1 4 6 2 3 5 2
  • 在 Yesod 生态系统中,对某些文本进行 urlencode 的最佳方式是什么?

    我想对一些文本进行 url 编码 例如 用 20 替换每个空格等 我找到了 HTTP Network HTTP Base urlEncode 并且可以使用它 但我想知道是否还有其他通常在 Yesod 生态系统中使用的东西 不幸的是 由于 U
  • Haskell 中的尾递归字符串分割

    我正在考虑分割字符串的问题s在一个字符处c 这表示为 break c s 其中 Haskell 库定义break c 足够接近 br br s h t if c h then s else let h t br t in h h t 假设我
  • Haskell Stack 从 github 安装包依赖项

    是否可以使用 Haskell 堆栈从 github 安装软件包的版本 例如在一个 cabal or a stack yaml文件 如何在 git repo branch revision 上指向依赖项 对于堆栈 The 的文档stack y
  • Haskell:不在范围内:数据构造函数

    今天开始在学校学习 haskell 我遇到了函数问题 我不明白为什么它不在范围内 代码如下 ff Char gt Char gt Char ff A B x 0 y 1 x lt A y lt B x 1 y 0 和错误 md31 hs 2
  • 使用fold_left/right反转OCaml中的列表

    更新 解决方案 感谢 jacobm 的帮助 我想出了一个解决方案 Folding Recursion let reverse list 3 theList List fold left fun element recursive call
  • ST monad 是如何工作的?

    我知道 ST monad 有点像 IO 的弟弟 而 IO 又是添加了状态 monadRealWorld魔法 我可以想象状态 也可以想象 RealWorld 以某种方式放入 IO 中 但每次我写一个类型签名ST the sST monad 的

随机推荐