
标题 2
这里是内容。
标题 3
这里是内容。

这里是内容。
这里是内容。

more 注释之前的内容被视为文章摘要。
本章我们将用第十章开发的图像分析库来制作一个条形码识别应用。只要用手机的摄像头拍下书的封底,我们就能用这个程序来提取这本书的ISBN编号。
市售绝大多数带有外包装的量产消费品上都有一个条形码。尽管有很多种不同的条形码系统在多个专业领域中被使用,但是在消费品中最典型的条形码系统还是UPC-A和EAN-13两种。UPC-A由美国开发,而EAN-13最初由欧洲开发。
EAN-13发表于UPC-A之后,它是UPC-A的超集。(事实上,尽管UPC-A现在在美国依然被广泛使用,但该标准早在在2005年已经被官方宣布作废了。)任何可以识别EAN-13条形码的硬件都可以兼容UPC-A条形码。这样我们只介绍EAN-13一种标准就可以了。
我们首先要面对的问题,是如何在某个 可能 编码了数字的位置把这个数字找出来。在此,我们要做一些简单的假设。第一个假设是我们处理的对象是图像中的单一行,第二个假设是我们明确知道条形码左边缘位置,这个位置即条形码的起始位置。
::: {#run-length-encoding} :::
我们如何解决线条宽度的问题呢。答案就是对图像数据进行游程编码(run length encode)。
-- file: ch12/Barcode.hs
type Run = Int
type RunLength a = [(Run, a)]
runLength :: Eq a => [a] -> RunLength a
runLength = map rle . group
where rle xs = (length xs, head xs)
我们常常会跟一些以键为索引的无序数据打交道。
举个例子,UNIX 管理猿可能需要这么一个列表,它包含系统中所有用户的 UID,以及和这个 UID 相对应的用户名。这个列表根据 UID 而不是数据的位置来查找相应的用户名。换句话来说, UID 就是这个数据集的键。
Haskell 里有几种不同的方法来处理这种结构的数据,最常用的两个是关联列表(association list)和 Data.Map 模块提供的 Map 类型。
关联列表非常简单,易于使用。由于关联列表由 Haskell 列表构成,因此所有列表操作函数都可以用于处理关联列表。
在 7 中,我们讨论了 IO
monad,那时我们刻意把精力集中在如何与外界交互上,并没有讨论monad是什么。
在 7 中我们看到 IO Monad确实很好用;除了在语法上不同之外,在 IO monad中写代码跟其他命令式语言基本没有什么区别。
在前面的章节中,我们在解决一些实际问题的时候引入了一些数据结构,很快我们就会知道它们其实就是monads。我们想告诉你的是,在解决某些问题的时候,monad通常是一个非常直观且实用的工具。本章我们将定义一些monads并告诉你它有多么简单。
为一个文本文件或者不同类型的数据做语法分析(parsing),对程序员来说是个很常见的任务,在本书第198页"使用正则表达式"一节中,我们已经学习了 Haskell 对正则表达式的支持。对很多这样的任务,正则表达式都很好用。
不过,当处理复杂的数据格式时,正则表达式很快就会变得不实用、甚至完全不可用。比如说,对于多数编程语言来说,我们没法(只)用正则表达式去 parse 其源代码。
Parsec 是一个很有用的 parser combinator 库,使用 Parsec,我们可以将一些小的、简单的 parser 组合成更复杂的 parser。Parsec 提供了一些简单的 parser,以及一些用于将这些 parser 组合在一起的组合子。毫不意外,这个为 Haskell 设计的 parser 库是函数式的。
Monad提供了一种强大途径以构建带效果的计算。虽然各个标准monad皆专一于其特定的任务,但在实际代码中,我们常常想同时使用多种效果。
比如,回忆在第十章中开发的 Parse 类型。在介绍monad之时,我们提到这个类型其实是乔装过的 State monad。事实上我们的monad比标准的 State monad 更加复杂:它同时也使用了
Either
类型来表达解析过程中可能的失败。在这个例子中,我们想在解析失败的时候就立刻停止这个过程,而不是以错误的状态继续执行解析。这个monad同时包含了带状态计算的效果和提早退出计算的效果。
无论使用哪门语言,错误处理都是程序员最重要--也是最容易忽视--的话题之一。在Haskell中,你会发现有两类主流的错误处理:"纯"的错误处理和异常。
当我们说"纯"的错误处理,我们是指算法不依赖任何IO Monad。我们通常会利用Haskell富于表现力的数据类型系统来实现这一类错误处理。Haskell也支持异常。由于惰性求值复杂性,Haskell中任何地方都可能抛出异常,但是只会在IO monad中被捕获。在这一章中,这两类错误处理我们都会考虑。
让我们从一个非常简单的函数来开始我们关于错误处理的讨论。假设我们希望对一系列的数字执行除法运算。分子是常数,但是分母是变化的。可能我们会写出这样一个函数:
目前为止,我们讨论的大多数是高阶概念。 Haskell 也可以用于底层系统编程。完全可以使用 Haskell 编写使用操作系统底层接口的程序。
本章中,我们将尝试一些很有野心的东西:编写一种类似 Perl 实际上是合法的 Haskell 的"语言",完全使用 Haskell 实现,用于简化编写 shell 脚本。我们将实现管道,简单命令调用,和一些简单的工具用于执行由 grep 和 sed 处理的任务。
有些模块是依赖操作系统的。本章中,我们将尽可能使用不依赖特殊操作系统的通用模块。不过,本章将有很多内容着眼于 POSIX 环境。 POSIX 是一种类 Unix 标准, 如 Linux ,FreeBSD ,MacOS X ,或 Solaris 。Windows 默认情况下不支持 POSIX ,但是 Cygwin 环境为 Windows 提供了 POSIX 兼容层。