Haskell 中的融合是什么?

2024-04-09

我时不时地注意到 Haskell 文档中的以下内容: (例如在Data.Text https://hackage.haskell.org/package/text-1.2.2.1/docs/Data-Text.html):

受融合影响

What is fusion我该如何使用它?


一般来说,融合是指旨在消除中间数据结构的转换。你fuse函数调用会导致浪费内存分配到更有效的地方。在我看来,这实际上是 Haskell 纯粹的最大应用之一。而且你几乎不需要做任何事情就可以得到它,它通过 GHC 编译器免费提供。

哈斯克尔是纯粹的

Because Haskell is pure, we get this thing called referential transparency https://wiki.haskell.org/Referential_transparency, which (from the link), means that "expression always evaluates to the same result in any context"1. That means that I can do very general program level manipulations without changing what the program will actually output. For example, even without knowing what x, y, z and w are I always know that

 ((x ++ y) ++ z) ++ w

将评估为相同的事情

 x ++ (y ++ (z ++ w))

然而,第二个实际上会涉及更少的内存分配(因为x ++ y需要重新分配输出列表的整个前缀)。

重写规则

事实上,我们可以做很多这样的优化,而且,因为 Haskell 是纯粹的,我们基本上可以移动整个表达式(替换x, y, z, or w对于上面示例中计算为列表的实际列表或表达式没有任何改变)。这变成了一个相当机械的过程。

此外,事实证明,您可以想出许多高阶函数的等价形式(免费定理! https://www.mpi-sws.org/~dreyer/tor/papers/wadler.pdf)。例如,

map f (map g xs) = map (f . g) xs

无论f, g, and xs是(两侧在语义上相等)。然而,虽然该等式的两侧产生相同的值输出,但左侧的效率总是较差:它最终为中间列表分配空间map g xs,立即被丢弃。我们想告诉编译器,每当它遇到类似的情况时map f (map g xs),将其替换为map (f . g) xs。而且,对于 GHC 来说,这是通过重写规则 https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#rewrite-rules:

{-# RULES     "map/map"    forall f g xs.  map f (map g xs) = map (f.g) xs #-}

The f, g, and xs可以与任何表达式匹配,而不仅仅是变量(所以像map (+1) (map (*2) ([1,2] ++ [3,4]))变成map ((+1) . (*2)) ([1,2] ++ [3,4]). (似乎没有一个好的方法来搜索重写规则 https://stackoverflow.com/questions/38651602/searching-for-rewrite-rules,所以我编译了一个list https://gist.github.com/harpocrates/e95ce275a2220dfbd50b102e1e533556). 解释 GHC 重写规则的动机和工作原理。

这就是GHC的优化方式map?

事实上,不完全是。上面的事情是捷径融合 https://wiki.haskell.org/Short_cut_fusion。这个名字有点暗示了它的缺点:它的扩展性不太好,而且调试起来很烦人。您最终必须为相同公共功能的所有安排编写大量临时规则。然后,您希望重复应用重写规则能够很好地简化您的表达式。

事实证明,在某些情况下,我们可以通过组织重写规则来做得更好,以便我们构建一些中间范式,然后制定针对该中间形式的规则。这样,我们开始获得重写规则的“热”路径。

这些系统中最先进的可能是针对共归纳序列(基本上是惰性序列,如列表)。查看这篇论文 http://community.haskell.org/~duncan/thesis.pdf and 这张纸 http://research.microsoft.com/en-us/um/people/simonpj/papers/ndp/haskell-beats-C.pdf(这实际上就是vector http://hackage.haskell.org/package/vector包已实施)。例如,在vector,您的代码首先转换为涉及的中间形式Streams and Bundles,以这种形式进行优化,然后转换回向量。

And... Data.Text?

Data.Text使用流融合来最小化发生的内存分配数量(我认为这对于严格变体尤其重要)。如果您查看source https://hackage.haskell.org/package/text-1.2.2.1/docs/src/Data-Text.html#pack,你会看到“受融合”函数实际上操纵Streams https://hackage.haskell.org/package/text-1.2.2.1/docs/Data-Text-Internal-Fusion.html大部分(它们的一般形式unstream . (stuff manipulating stream) . stream)还有一堆RULES用于转换的实用指令Streams。最后,这些函数的任意组合都应该被融合,以便只需要进行一次分配。

那么,我的日常编码需要注意什么?

了解代码何时需要融合的唯一真正方法是充分了解所涉及的重写规则并充分了解 GHC 的工作原理。也就是说,有一件事你should做:尽可能尝试使用非递归高阶函数,因为它们可以(至少目前如此,但通常总是更容易)轻松融合。

并发症

因为 Haskell 中的融合是通过重复应用重写规则而发生的,所以只要知道整个“融合”程序与原始程序执行相同的操作,就足以让自己相信每个重写规则的正确性。除了与程序终止相关的边缘情况。例如,有人可能认为

 reverse (reverse xs) = xs

但这显然不是真的,因为head $ reverse (reverse [1..])还不会终止head [1..] will. 来自 Haskell Wiki 的更多信息 https://wiki.haskell.org/Correctness_of_short_cut_fusion.


1 This is actually true only provided that in these contexts the expression maintains the same type.

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

Haskell 中的融合是什么? 的相关文章

  • 这个记忆的斐波那契函数是如何工作的?

    在我正在做的函数式编程课程的当前练习作业中 我们必须制作给定函数的记忆版本 为了解释记忆化 给出以下示例 fiblist fibm x x lt 0 fibm 0 0 fibm 1 1 fibm n fiblist n 1 fiblist
  • 澄清创建临时表的连接顺序

    我在 mysql 中有一个大型查询 涉及将多个表连接在一起 它太慢了 所以我做了 解释 发现它正在创建一个临时表 我怀疑它占用了大部分执行时间 我找到了一些相关资料 mysql 文档 http dev mysql com doc refma
  • 无点镜头创建不进行类型检查

    在函数中test 我遍历一个列表 从它的成员生成镜头 然后打印一些数据 当我使用有针对性的呼叫风格时 这会起作用 当我使其成为无点时 它无法进行类型检查 为什么会出现这种情况 我该如何解决这个问题 在我看来 GHC 并没有保留排名较高的信息
  • Haskell scala 互操作性

    我是 Scala 初学者 来自面向对象范式 在了解 Scala 的函数式编程部分时 我被引导到 Haskell 纯函数式编程语言 探索 SO 问题答案 我发现 Java Haskell 具有互操作性 我很想知道 Scala Haskell
  • 我应该在 Turtle 或 Foldl 包中使用折叠吗?

    我在使用 Turtle 时遇到了一些困难 直到盯着难以理解的错误消息几分钟后才意识到我使用了错误的fold功能 https hackage haskell org package turtle 1 5 8 docs Turtle Shell
  • 在 Haskell 中,为什么我必须在这段代码中使用美元符号?

    我仍在尝试破解这段代码 import Data Char groupsOf groupsOf n xs take n xs groupsOf n tail xs problem 8 x maximum map product groupsO
  • 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
  • Haskell:是的,没有类型类。为什么是整数?

    我有一个关于 GHCi 如何假定整数类型的问题 我正在阅读 Learn you a Haskell 是 否类型的课程 如果您想阅读全文 这里有一个链接 http learnyouahaskell com making our own typ
  • 如何在 Haskell 中向右或向左移动列表的 1 个元素?

    嗨 我一直在寻找答案 但找不到 假设我们有一个像这样的列表 1 10 4 5 3 我怎样才能将 5 向左移动 使这个列表变成 1 10 5 4 3 我尝试过了swapElementsAt通过找到该元素的索引 但它看起来非常不足 swapEl
  • 在依赖类型的函数式编程语言中,扁平化列表是否更容易?

    在 haskell 中寻找一个可以展平任意深度嵌套列表的函数时 即应用的函数concat递归并在最后一次迭代时停止 使用非嵌套列表 我注意到这需要有一个更灵活的类型系统 因为随着列表深度的变化 输入类型也会变化 确实 有几个 stackov
  • 如何为 CUDA 内核选择网格和块尺寸?

    这是一个关于如何确定CUDA网格 块和线程大小的问题 这是对已发布问题的附加问题here https stackoverflow com a 5643838 1292251 通过此链接 talonmies 的答案包含一个代码片段 见下文 我
  • 将 num 的签名键入 double?

    我才刚刚开始为你学习 Haskell 以获得伟大的好处 并且我在类型类方面遇到了一些麻烦 我想创建一个接受任何数字类型并强制其为双精度的函数 我的第一个想法是定义 numToDouble Num gt Double 但我认为这不起作用 因为
  • Haskell,堆栈:找到可执行文件

    我正在寻找类似的东西 stack whereis hasktags where whereis行为或多或少类似于 UNIXwhereis命令 hasktags是这样运行的 stack exec hasktags stack exec whe
  • Haskell 中的分类结构

    Hask通常被认为是一个范畴 其对象是类型 态射是函数 然而 我看到 Conor McBride pigworker 警告不要使用Hask多次 1 https stackoverflow com a 45905082 474311 2 ht
  • 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
  • 标准的能力

    我发现了一些使用标准的旧例子here http www serpentine com blog 2009 09 29 criterion a new benchmarking library for haskell 看起来好像早在 2009
  • QuickCheck是否可以生成任意函数

    我试图为身份编写一个 QuickCheck 测试 f y f y 我最初的计划是编写一个返回函数和整数的任意生成器 具有签名Gen Int gt Int Int 并在prop DollerDoesNothing使用 不使用测试该功能应用程序
  • 有什么工具可以说明每种方法运行需要多长时间?

    我的程序的某些部分速度很慢 我想知道是否有我可以使用的工具 例如它可以告诉我可以运行 methodA 花了 100ms 等等 或者类似的有用信息 如果您使用的是 Visual Studio Team System 性能工具 中有一个内置分析
  • 如何通过“cabal build”或“stack build”构建带有图标的项目

    我想构建一个带有图标的可执行文件 通过谷歌搜索 我发现这里的说明 https wiki haskell org Setting an executable icon 但它只能通过编译源文件来工作ghc 如果我想构建一个具有可执行文件的项目c
  • 使用 z = f(x, y) 形式的 B 样条方法来拟合 z = f(x)

    作为一个潜在的解决方案这个问题 https stackoverflow com questions 76476327 how to avoid creating many binary switching variables in gekk

随机推荐

  • 带有 TypeScript 和 node_modules 的 Visual Studio Resharper

    我正在使用 Visual Studio 2015 Update 3 当我尝试使用 Resharper 重构我的 TypeScript 代码时 Resharper 会尝试重构每个文件夹中的代码 包括node modules 显然 我不想让Re
  • 如何在 DI 扩展类中加载、处理和使用 Yaml 配置文件中的自定义参数?

    我正在尝试按照此处提供的文档在我的应用程序中导入 yaml 配置文件http symfony com doc current bundles extension html http symfony com doc current bundl
  • Android 远程或推送配置文件

    我正在寻找一种通过 Web URL 链接配置 Android 邮件帐户的解决方案 Android 设备管理 API 自 2 2 起 限制非常严格 不包括邮件帐户配置 在iOS上 有配置文件系统 您只需下载它即可配置您的 iOS 设备 但是对
  • atom-typescript 抱怨 tsconfig.json——我怎样才能自动创建一个?

    我刚刚开始使用此处引用的 atom typescript 插件 TypeScript 入门 http basarat gitbooks io typescript content docs getting started html 该页面指
  • Access VBA随机改变大小写

    我在 MS Access 中有一个编码项目 有一些开发人员编码并将源代码签入 SVN 服务器 由于 SVN 服务器用于管理代码 因此它可以识别源代码文件中的更改 这些源代码文件中存在问题 VBA经常改变大小写字母 但我们不知道为什么 当我进
  • 反思 ExpandoObject

    我写了一个漂亮的函数 它将接受system object 反映其属性并将对象序列化为 JSON 字符串 它看起来像这样 public class JSONSerializer public string Serialize object o
  • Android 指纹删除后密钥失效

    我正在 7 1 1 上的 Google Pixel 设备上进行测试 发现从设备中删除所有指纹后 我的私钥并未失效 我已经根据演示应用程序使用单个对称 SecretKey 进行了测试 并且按预期工作 但是使用非对称密钥对只会引发KeyPerm
  • React Hooks:用于模式事件监听器的 useEffect

    我有一个模式对话框 如果用户在模式之外单击 我想关闭该对话框 我编写了以下 useEffect 代码 但遇到了以下问题 模式对话框包含许多子项 React Nodes 这些子项可能会发生变化 例如 用户删除列表中的条目 这些交互触发我的 o
  • 滑动动画存在多个视图同步问题

    我正在尝试用两个文本视图制作动画 两者都处于相对布局中 动画的功能是左文本视图会向左移动一点 同时右文本视图也会向左移动一点 我努力了 http nineoldandroids com http nineoldandroids com an
  • 发布配置文件未在 TFS Build 上部署

    我在 VS2012 中有一个 net4 解决方案 它有一个带有自己的发布配置文件的网站 当从 VS 中执行时 配置文件成功通过 webdeploy 发布 但当使用 TFS2012 构建时 它似乎被忽略 我将这些 MSBuild 参数传递到构
  • 如何在IOS中制作月亮绕地球旋转并同时自己旋转的CAAnimation效果?

    我知道在IOS中创建月亮绕地球转的效果很简单 假设月球是一个 CALayer 对象 只需将该对象的锚点更改为地球 它就会以动画方式围绕地球旋转 但如何创造出同时自转的月亮呢 由于月亮只能有一个锚点 看来我无法再让这个 CALayer 对象自
  • ViewPager Fragment 再次重新加载时消失

    以下是布局 test xml
  • 如何检查线段和从与水平面成一定角度的点发出的线射线之间的交点?

    给定一条线段 即两个点 x1 y1 和 x2 y2 一个点 P x y 和一个角度 theta 我们如何判断这条线段和从 P 发出的 与水平方向成 角的线射线是否相交 如果它们相交 如何找到交点 我们来标记点q x1 y1 and q s
  • 替换现有的 Outlook 日历约会

    我正在与icalndar约会一代做一些工作 这将允许代表查看活动的网站并单击提供的链接将约会添加到他们的日历中 我有一个工作程序集 它将根据一组已知的信息 开始日期 结束日期 标题等 生成 ics 格式的输出 作为物理文件或流 我为日历约会
  • 方法声明应与 PHP 中的父方法兼容

    Strict Standards Declaration of childClass customMethod should be compatible with that of parentClass customMethod PHP 中
  • 避免 InvalidOperationException 的最佳实践:集合已修改?

    很多时候我需要这样的东西 foreach Line line in lines if line FullfilsCertainConditions lines Remove line 这不起作用 因为我总是得到一个InvalidOperat
  • Highcharts 系列更新动画

    我可以使用此方法更新蜘蛛图的数据值并查看其动画 chart series i setData newSeries i data 但是 由于蜘蛛图中的系列不仅包括data还有其他领域 例如 series name Allocated Budg
  • 如何在容器内放置旋转图像?

    使用 CSS3 HTML 如果需要的话还可以使用 javascript jquery 我需要将图像旋转 90 270 度并使其位置填充其父 div 容器 听起来很简单 但是当图像旋转时 位置会发生变化 我不知道如何或为什么 这是一个 jsF
  • 仅当某个字符不直接跟在另一个特定字符之后时才拆分该字符串

    我有以下代码行来分割字符串data2出现空白实例时进入列表 string list data2 split 但是 在我的一些数据中 日期格式为 28 Dec 这里 上面的代码在我不希望的情况下在日期和月份之间的空白处进行了分割 有没有办法我
  • Haskell 中的融合是什么?

    我时不时地注意到 Haskell 文档中的以下内容 例如在Data Text https hackage haskell org package text 1 2 2 1 docs Data Text html 受融合影响 What is