《Haskell并行与并发编程》——第2章,第2.4节Deepseq

2.4 Deepseq
Haskell并行与并发编程
前面已经用过了force函数,其具体类型如下:

force :: NFData a => a -> a

函数force会对其参数完全求值,然后返回。不过该函数并非内建,而是针对每种数据类型,通过NFData类对该函数的行为进行定义。NFData的意思是范式数据(normal-form data),其中范式是指不包含未被求值子表达式的值,“数据”则表示范式中不包含函数,因为无法看到函数里面的内容并对里面的内容求值1。

类NFData仅包含一个方法:

class NFData a where
rnf :: a -> ()
rnf a = a
seq()
函数rnf名字的意思是“约化为范式”(reduce to normal-form)。该函数对其参数完全求值,然后返回()。默认通过seq来实现,对于没有子结构的类型来说很方便,只需使用默认定义即可。例如:Bool的实例可以简单地定义:

instance NFData Bool
模块Control.Deepseq对库中所有其他的常见类型均提供了实例。

对于自定义的类型,可能需要实现相应的NFData的实例。例如,对于二叉树类型:

data Tree a = Empty | Branch (Tree a) a (Tree a)

那么,其NFData的实例如下:

instance NFData a => NFData (Tree a) where
  rnf Empty = ()
  rnf (Branch l a r) = rnf l `seq` rnf a `seq` rnf r

实现的思路为递归地对数据类型的组成部分应用rnf,然后将这些rnf的调用通过seq组合起来。

Control.DeepSeq模块中还有一些其他运算,例如:

deepseq :: NFData a => a -> b -> b
deepseq a b = rnf a `seq` b

函数deepseq因其和seq的相似性而得名:和seq类似,都是强制求值。如果把弱首范式看成是浅(shallow)求值,那么范式就是深(deep)求值,因此得名deepseq。

函数force是通过deepseq定义的:

force :: NFData a => a -> a
force x = x `deepseq` x

函数force的功能应该被认为是将WHNF转换为NF(normal form),也就是,当程序将force x求值成WHNF时,x会被求值成NF。

图像说明文字将表达式求值为范式需要对整个数据结构进行遍历,因此需要铭记,对于大小为n的数据结构,其复杂度是O(n),而对seq来说,只有O(1)。因此,应该避免对同一数据反复使用force或deepseq函数。

WHNF和NF就像天平的两端,但是,根据数据类型的不同,还有许多处于中间的“不同程度的求值”。例如:前面的length函数对列表的脊(spine)求值,即展开了列表,但未对列表的元素求值。parallel包中的模块Control.Seq提供了一系列组合子,通过组合,可以对数据结构达到不同程度的求值。本书虽然不会在例子中使用这些运算,但对读者也许会有所帮助。

1不过,纯粹为了方便,定义了一个函数的NFData实例,会将函数求值为WHNF(弱首范式),因为经常会有数据结构里面包含函数,而尽管如此,还是想尽可能地对这样的数据结构进行求值。

时间: 2024-11-10 00:16:42

《Haskell并行与并发编程》——第2章,第2.4节Deepseq的相关文章

《Haskell并行与并发编程》——第2章,第2.1节惰性求值和弱首范式

2.1 惰性求值和弱首范式 Haskell并行与并发编程 Haskell是一门惰性语言,即表达式是在其值需要使用时才被求值2.一般来说,不必担心该过程如何发生,只要表达式在需要时求值,不需要时不被求值即可.但是,当在代码中使用了并行编程后,就需要告诉编译器一些程序运行的信息,即某些代码应该并行执行.由于对惰性求值的工作方式有一个直觉的认识将有助于有效地进行并行编程,因此本节将以GHCi作为试验工具,探讨惰性求值的一些基本概念. 下面从非常简单内容的开始:Prelude> let x = 1 +

《Haskell并行与并发编程》——第1章,第1.2节工具和资源

1.2 工具和资源 Haskell并行与并发编程 为了运行本书中的范例程序和完成练习,需要安装Haskell Platform(http://hackage. haskell.org/platform/).Haskell Platform中包含了GHC编译器和所有重要的库,包括这里需要使用的并行和并发库.本书中的代码在2012.4.0.0版Haskell Platform上测试,但是示例代码会随着新的版本发布而进行更新. 有几章需要安装额外的软件包.安装这些额外的依赖的指令可以在1.3节中找到.

《Haskell并行与并发编程》——第1章,第1.1节学习术语:并行性和并发性

1.1 术语:并行性和并发性 Haskell并行与并发编程 在许多领域并行和并发是同义词,但在编程中则不然,它们被用来描述在根本上完全不同的两个概念. 并行程序是指使用多个硬件参与计算(如多个处理器核心)使之更快的程序.目标是通过将计算的不同部分分配给不同的处理器,使之能够同时执行,从而更早得到问题的答案. 与之相对的,并发则是一种包含多个控制线程的程序构成技术.从概念上说,这些控制线程是"同时"被执行的,也就是说,用户所观察到的最终影响,是由这些线程交替作用产生的.到底是否真的是同时

《Haskell并行与并发编程》——第2章,第2.2节Eval monad、rpar和rseq

2.2 Eval monad.rpar和rseq Haskell并行与并发编程 下面介绍模块Control.Parallel.Strategies提供的用于并行编程的一些基本内容,定义如下: data Eval a instance Monad Eval runEval :: Eval a -> a rpar :: a -> Eval a rseq :: a -> Eval a 并行性是通过Eval monad表达的,具体包括rpar和rseq两个运算.组合子rpar用于描述并行,即其参

《Haskell并行与并发编程》——第2章,第2.3节示例:并行化数独解算器

2.3 示例:并行化数独解算器 Haskell并行与并发编程 本节将分析一个实例,考察如何并行化一个程序,使之对多组输入数据执行相同的计算.该计算实现了数独求解的功能.解算器(solver)速度相当快,能在2分钟内求解所有给定了17个已知数的共49 000个谜题. 仅作为需要的对多组输入数据执行大量相同计算的例子,对于解算器如何工作的细节,这里并不感兴趣.这里进行讨论的目的是并行化解算器,使之能够同时解多个谜题.出于该目的,解算器将被当作黑盒处理. 解算器在模块Sudoku中实现,模块提供了一个

《Haskell并行与并发编程》——导读

目 录第 1 章 绪论 1.1 术语:并行性和并发性1.2 工具和资源 1.3 示例代码 第一部分 并行Haskell 第 2 章 并行基础:Eval Monad 2.1 惰性求值和弱首范式 2.2 Eval monad.rpar和rseq 2.3 示例:并行化数独解算器 2.4 Deepseq 第 3 章 求值策略 第 4 章 数据流并行:Par Monad 第 5 章 Repa数据并行编程 第 6 章 Acellerate GPU编程 第二部分 并发Haskell 第 7 章 并发基础:线程

《C++并发编程实战》——第1章 你好,C++并发世界

第1章 你好,C++并发世界 C++并发编程实战本章主要内容 何谓并发和多线程为什么要在应用程序中使用并发和多线程C++并发支持的发展历程一个简单的C++多线程程序是什么样的这是令C++用户振奋的时刻.距1998年初始的C++标准发布13年后,C++标准委员会给予程序语言和它的支持库一次重大的变革.新的C++标准(也被称为C++11或C++0x)于2011年发布并带来了很多的改变,使得C++的应用更加容易并富有成效. 在C++11标准中一个最重要的新特性就是支持多线程程序.这是C++标准第一次在

《C++ 并发编程》- 第1章 你好,C++的并发世界

本文是<C++ 并发编程>的第一章感谢人民邮电出版社授权并发编程网发表此文版权所有请勿转载.该书将于近期上市. 本章主要内容 何谓并发和多线程  为什么要在应用程序中使用并发和多线程  C++并发支持的发展历程  一个简单的C++多线程程序是什么样的 这是C++用户的振奋时刻.距1998年初始的C++标准发布13年后C++标准委员会给予程序语言和它的支持库一次重大的变革.新的C++标准也被称为C++11或C++0x于2011年发布并带来了很多的改变使得C++的应用更加容易并富有成效. 在C++

《Java并发编程的艺术》第一章

作者:方腾飞  本文是样章  购买本书=>  当当 京东 天猫 互动 第1章并发编程的挑战 并发编程的目的是为了让程序运行的更快,但是并不是启动更多的线程,就能让程序最大限度的并发执行.在进行并发编程时,如果希望通过多线程执行任务让程序运行的更快,会面临非常多的挑战,比如上下文切换的问题,死锁的问题,以及受限于硬件和软件的资源限制问题,本章会介绍几种并发编程的挑战,以及解决方案. 1.1     上下文切换 即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制