引 言

词频统计

如同格诺的故事,本书涉及的计算任务很简单:给定一个文本文件,显示出现频率最高的N(如25)个单词,并且按词频由高到低进行排序。我们将大写字母转换成相应的小写字母,并且忽略the、for等停止词。简单起见,对出现次数相同的词的排序不作任何规定。通常,我们称这项任务为词频统计任务。

看一个输入输出样例:

输入:

White tigers live mostly in India  
Wild lions live mostly in Africa

输出:

live - 2
mostly - 2
africa - 1
india - 1
lions - 1
tigers - 1
white - 1
wild - 1

如果对古登堡数字电子书计划中的简•奥斯汀的《傲慢与偏见》做同样的词频统计,会得到如下输出:

mr - 786
elizabeth - 635
very - 488
darcy - 418
such - 395
mrs - 343
much - 329
more - 327
bennet - 323
bingley - 306
jane - 295
miss - 283
one - 275
know - 239
before - 229
herself - 227
though - 226
well - 224
never - 220
sister - 218
soon - 216
think - 211
now - 209
time - 203
good - 201

本书将介绍这项词频统计任务。另外,每一章都会有练习,其中的一个练习是用该章所描述的风格编写其他简单的任务。以下是一些建议。

这些任务对于任何一名高年级学生都是非常简单的,考量的重点在于遵循每种风格中的限制,而非算法。

单词索引

给定一个文本文件,按字母顺序输出所有单词,连同每个单词出现的页码。忽略所有出现100次以上的单词。假定每45行为一页。以《傲慢与偏见》为例,前几行输出应当为:

abatement - 89  
abhorrence - 101, 145, 152, 241, 274, 281  
abhorrent - 253  
abide - 158, 292  
...

单词上下文

给定一个文本文件,对于给定的单词列表,按字母顺序输出单词及其上下文,连同出现的页码。假定每45行为一页,上下文为单词出现处前两个单词及后两个单词,忽略标点符号。以《傲慢与偏见》为例,concealment和hurt相对应的输出为:

perhaps this concealment this disguise - 150
purpose of concealment for no - 207
pride was hurt he suffered - 87
must be hurt by such - 95
and are hurt if i - 103
pride been hurt by my - 145
must be hurt by such - 157
infamy was hurt and distressed - 248

建议在此项任务中使用如下单词:concealment、discontented、hurt、agitation、mortifying、reproach、unexpected、indignation、mistake和confusion。

Python

本书的示例程序均用Python编写,但理解风格并不需要精通Python。事实上,每章的练习都包括用其他语言重写示例程序。因此,读者只需要能够读Python,而非写Python。

Python的代码很容易阅读,但语言中的若干特例可能会让使用其他语言的读者感到困惑。我在这里做一下简单介绍。

 列表。列表是使用特定语法的基础数据结构,和类C语言中的数组类似,例如mylist = [0, 1, 2, 3, 4, 5]。Python并没有数组类型①,大多数在类C语言中使用数组的场景,在Python中会使用列表。

 元组。元组是不可修改的列表。元组同样也是使用特定语法的基础数据结构,和类Lisp语言中的列表类似,例如mytuple = (0, 1, 2, 3, 4)。列表和元组的处理方式相似,只是元组不可修改,因此所有对列表有修改的操作对元组并不适用。

 列表下标。列表和元组的元素可以通过下标来访问:mylist[some_index]。列表的下标下限和类C语言一样,为0。列表长度可以通过len(mylist)得到。列表的索引方式比这个简单的例子更丰富,以下是一些例子。

mylist[0]:列表的第一个元素

mylist[-1]:列表的最后一个元素

mylist[-2]:列表的倒数第二个元素

mylist[1:]:第二个元素至最后的子列表

mylist[1:3]:第二个元素至第四个元素的子列表

mylist[::2]:包含所有偶数下标元素的子列表

 mylist[start:stop:step]:从start下标开始,在[start, stop]下标之间,每隔step个元素取一个而得到的子列表

  ① Python中有数组类型,但不是语言原生的数据类型,且没有特殊的语法。不如列表常用。

 上下界。获取在列表长度之外的下标元素会导致下标错误。例如,在一个长度为3的列表中获取第4个元素(如:[10, 20, 30][3])会导致下标错误。然而,许多针对列表(和集合)的Python操作在索引方面会做额外处理。例如,在一个长度为3的列表中获取第4个至第101个元素(如:[10, 20, 30][3:100])会返回一个空列表([]),而不是下标错误。类似地,当所需区间部分覆盖了列表的取值范围,Python会返回包含覆盖部分元素的列表,而不会返回任何错误(如:[10, 20, 30][2:10] 会返回[30])。这种自适应的处理方式可能会让用其他没有这种特性的语言编程的人非常困惑。

 字典。在Python中,字典(或称映射)也是使用特定语法的基础数据结构,例如mydict = {'a': 1, 'b': 2}。这个字典将两个字符串映射成两个整型。通常来说,键与值可以是任何数据类型。在Java中,这种数据类型可以在HashMap类中找到;在C++中,表达形式会是map模板。

 self。在大部分面向对象语言中,一般都会有特殊的语法来表达对象对于自己的引用,如Java和C++中的this、PHP中的$this以及Ruby中的@。与这些语言不同的是,Python没有这种特殊语法。同时,实例方法的第一个参数为一个对象,通常会被称为self,但并不是强制的,如以下例子:

  1    class Example: 
  2        def set_name(self, n):
  3            self._name = n
  4        def say_my_name(self):
  5            print self._name

两个成员方法的第一个参数均为self,都在方法内被引用。self这个词本身并不特殊,可以使用其他任何名字,如me、my甚至this,但任何self以外的词都会为Python程序员所反感。而在调用成员方法时,会有些许令人意外,因为第一个参数被省略了:

e  = Example()
e.set_my_name("Heisenberg")
e.say_my_name()

这种参数个数上的不匹配,是因为点(.)在Python中是一种简单的语法糖(syntactic sugar),更原始的写法应当是:

e = Example()
Example.set_my_name(e, "Heisenberg")
Example.say_my_name(e)

 构造函数。在Python中,构造函数是名为init(init两边各有两条下划线)的普通函数。在对象实例被创建之后,这个函数会被自动调用,如以下例子:

  1    class Example:
  2        # 这是本类的构造函数
  3        def __init__(self, n):
  4            self._name = n
  5        def say_my_name(self):
  6            print self._name
  7          
  8    e = Example("Heisenberg")
  9    e.say_my_name()

目录

  • 前 言
  • 引 言
  • 第一部分 悠久历史
  • 第1章 往日的美好
  • 第2章 Forth风格
  • 第二部分 基本风格
  • 第3章 单片风格
  • 第4章 食谱风格
  • 第5章 流水线风格
  • 第6章 高尔夫风格
  • 第三部分 函数组合
  • 第7章 无限镜像风格
  • 第8章 骨牌风格
  • 第9章 单子风格
  • 第四部分 对象与对象交互
  • 第10章 对象风格
  • 第11章 消息风格
  • 第12章 闭域风格
  • 第13章 抽象对象风格
  • 第14章 好莱坞风格
  • 第15章 公告板风格
  • 第五部分 反射与元编程
  • 第16章 内省风格
  • 第17章 反射风格
  • 第18章 切面风格
  • 第19章 插件风格
  • 第六部分 异常处理
  • 第20章 构建风格
  • 第21章 Tantrum风格
  • 第22章 消极攻击风格
  • 第23章 声明意图风格
  • 第24章 隔离风格
  • 第七部分 以数据为中心
  • 第25章 持久表风格
  • 第26章 试算表风格
  • 第27章 漂流风格
  • 第八部分 并发
  • 第28章 参与者风格 
  • 第29章 数据空间风格
  • 第30章 Map Reduce风格
  • 第31章 双重Map Reduce风格 
  • 第九部分 交互
  • 第32章 三层架构风格
  • 第33章 RESTful风格