函数式程序设计为什么至关重要

Why Functional Programming Matters 函数式程序设计为什么至关重要

作者: John Hughes 原文地址:http://wiht.link/functional-prog

此论文作于1984年,作为查麦兹大学的备忘录流传了多年,经过小幅度修订的版本出现于1989年与1990年,即[Hug89]与[Hug90]。此版本基于原查麦兹大学备忘录的nroff源码,为LaTeX做了改动,使其更接近于印刷版本并纠正了少许错误。

本文由CloudiDust在 http://blog.csdn.net/ddwn/article/details/984390 最初翻译,并由BYVoid根据原文重新校对、整理和排版。

摘要

软件正在变得越来越复杂,因此良好的软件构架也越来越重要。结构良好的软件易于编写,易于除错,同时提供可复用组件库以降低未来开发的成本。传统型语言在程序模块化方面具有理念上的局限性,而函数式语言超越了局限。在本文中我们指出,函数式语言的两大特性,高阶函数与惰性求值,能够极大地促进模块化。作为例证,我们处理了列表和树,编写了一些数值算法,并实现了alpha-beta启发式搜索(一个人工智能算法,用于游戏系统中)。既然模块化是程序设计成功的关键,那么函数式语言对现实世界而言便极其重要了。

1.引言

本论文试图证明,对“现实世界”而言,函数式程序设计是极其重要的。同时,本文也试图明确指出函数式语言的长处,以帮助使用函数式语言的程序员们将这些长处发挥到极致。

函数式语言之所以被如此称呼,是因为程序完全是由函数组成的。主程序本身也是一个函数,以程序的输入为参数,并返回其输出。典型地,主函数通过其他函数定义,而这些函数又同样以更多的其他函数来定义,直到最低层的语言原生函数为止。这些函数与数学中的函数很相像,因此在本文中将以普通等式来定义它们。本文使用了Turner的程序语言Miranda中的表示方法,但对于之前没有函数式语言相关知识的读者,本文仍然是可读的。(Miranda是Research Software Ltd.的商标。)

函数式程序设计的特性与优点通常总结为类似这样:函数式程序不包含任何赋值语句,因此变量一旦被赋予一个值,就不再改变。更一般地说,函数式程序不包含任何副作用:一个函数除了计算它本身的值以外,不产生任何作用。这一特性消灭了“Bug”的一个主要来源,同时也使执行顺序不再重要——没有副作用能够改变一个表达式的值,故它可以在任何时刻被求值。这一特性将程序员从决定控制流的重担之下拯救出来。由于表达式可以在任何时刻被求值,程序员便可以随心所欲地使用变量的值来代替变量,反之亦然——也就是说,程序是“引用透明”的。这一自由使得函数式程序与它们传统的对应物相比,更容易数学化地控制。

这样的“优点”列表很不错,但如果说外行人不把它当回事,这也并不会令人惊讶。它列出了很多内容关于函数式程序设计“没有”什么(它没有赋值,没有副作用,没有控制流)但却没多说它“有”什么。函数式程序员听起来很像是中世纪的僧侣似的,他们禁绝了尘世中的种种乐趣并且期望这能使自己变得高洁。对于那些更关心物质利益的人而言,这些“优点”并没有多大的说服力。

函数式程序员们争辩说,函数式程序设计确实有巨大的物质利益——一个函数式程序员拥有比他传统型的同行高得多的生产力,因为函数式程序短得多。但这有什么道理吗?在这些“优点”的基础之上,唯一的很靠不住的借口就是,传统的程序中有90%是赋值语句,而在函数式程序中这些全都可以省略!这真是太荒唐了,如果省略赋值语句可以带来如此巨大的好处,那么FORTRAN程序员们早该这样干了二十年了。通过省略特性来使语言更加强大在逻辑上是不可能的,不论这种特性是多么糟糕。

甚至函数式程序员都应该对这些所谓的“优点”表示不满意,因为它们对于发掘函数式语言的威力毫无帮助。不可能写出一个特别地(particularly)缺少了赋值语句或者特别地引用透明的程序。这些不是什么衡量程序质量的尺度,因此盯紧了它们(以此证明函数式语言的强大)也不理想。

很明显,对函数式程序设计的特性的描述是不完备的。我们必须找出一些东西来填补——它们不但要解释函数式程序设计的威力,更要给函数式程序员们一个明确的追求目标。

2.与结构化程序设计的相似性

指出函数式与结构化程序设计之间的相似性是很有帮助的。过去,结构化程序设计的特性与优点被总结为类似这样:结构化程序不包含goto语句;结构化程序中的语句块没有多入口与多出口;结构化程序与它们传统的对应物相比,更容易数学化地控制。这些结构化程序设计的“优点”与我们之前所谈到的函数式程序设计的“优点”在本质上很相似。这些叙述本质上都是否定式的,从而导致了诸如“不可或缺的goto”之类一大堆徒劳的争论。

事后诸葛亮式地说,很明显地,结构化程序设计的这些特性,尽管很有用,但没有触及问题的核心。结构化程序与非结构化程序之间最重要的区别就是,结构化程序是用模块化的方法设计的。模块化设计带来了生产力的巨大提升:首先,小模块可以很快很容易地编写;其次,通用模块可以被重用,使以后的程序可以更快地开发;再次,程序的模块可以被独立测试,减少了除错的时间。

“不使用goto”等等这一类特性,对于这一提升没什么作用。这些特性促进了“程序设计的小改良”,然而模块化设计却促进了“程序设计的大进化”。因此,程序员在FORTRAN或汇编语言中都可以享受结构化程序设计带来的好处,哪怕那需要一点额外的工作。

模块化设计是成功的程序设计的关键,这一观点现在已经被普遍地接受了,而诸如Modula-II[Wir82],Ada[oD80]以及Standard ML[MTH90]之类的程序语言都内置了语言特性以促进模块化。然而,有一点非常重要,却常常被忽略。当编写一个模块化程序以解决问题的时候,程序员首先把这个问题分解为子问题,而后解决这些子问题并把解决方案合并。程序员能够以什么方式分解问题,直接取决于他能以什么方式把解决方案粘起来。因此,为了能在观念上提升程序员将问题模块化的能力,必须在程序语言提供中提供各种新的黏合剂。复杂的作用域规则与对分块编译的支持只对文本层面的细节有帮助,它们没有提供能表达新观念的工具以分解问题。

通过与木匠行业的类比可以认识到黏合剂的重要性。先制作椅子的各部分——坐垫,椅子腿,靠背,等等——而后用正确的方法钉起来,那么制作一把椅子是很容易的。但这取决于将木板与插接口结合起来的能力。如果缺乏这种能力,那么制作椅子的唯一方式,就是将它从一大块木头里整个地切割出来,这是一项艰巨得多的任务。这个例子同时表明了模块化的非凡威力与拥有合适的黏合剂的重要性。

现在让我们回到函数式程序设计上来。在这篇论文余下的部分里,我们将指出,函数式语言提供了两种新的、非常重要的黏合剂。我们将给出许多可以使用新方法模块化的示例程序,它们因此变得很简洁。这就是函数式程序设计威力的关键——它允许了大幅改进的模块化设计。这也正是函数式程序员必须追求的目标——更小、更简洁、更通用的模块,用我们将要描述的新黏合剂黏合起来。

3.把函数粘起来

两种黏合剂中的第一种,使简单的函数可以聚合起来形成复杂的函数。以一个简单的处理问题来说明:将列表中的元素累加起来。我们用下面的语句定义列表:

listof X :: = nil | cons X (listof X)

这说明,一个元素类型为X的列表(不论X是什么),或是nil,代表一个没有元素的空列表,或是一个X与另一个X的列表的conscons代表一个列表,其首元素为X,而第二个以及后续元素即是另一个X的列表的元素。此处的X可以代表任何类型——例如,如果X是一个Integer(整数类型),那么这个定义就是说,一个整数列表,或者是空的,或者是一个整数与另一个整数列表的cons。依照通常的实践,我们写列表时,只是简单地将其元素包含在方括号里,而不是将consnil显式地写出来。方便起见,这是一个简单的速记法。例如:

[] 表示 nil
[1] 表示 cons 1 nil
[1 2 3] 表示 cons 1 ( cons 2 ( cons 3 nil ))

列表中的元素可以通过一个递归函数sum进行累加。sum必须为两类参数进行定义:一个空列表(nil),以及一个cons。由于没有数字存在时,累加结果是0,因此我们定义:

sum nil = 0

又因为cons的累加和可以通过将列表的第一个元素加到其余元素的累加和上的方式进行计算,所以可以定义:

sum (cons num list) = num + sum list

检查定义可以发现计算sum时,只有下面用红色粗体标出的部分是特化的:

sum nil = 0

sum (cons num list) = num + sum list

这说明sum的计算可以通过一个通用的递归模式和特化的部分来模块化。这个通用的递归模式习惯上被称为“递减”(reduce),因此sum可以表达为:

sum = reduce add 0

方便起见,reduce传递了一个二元函数addadd是这样定义的:

add x y = x + y

只要将sum的定义参数化,我们便可以得到reduce的定义,即:

(reduce f x) nil = x
(reduce f x)(cons num list) = f num ((reduce f x) list)

这里我们写出了(reduce f x)两边的括号以强调它代替了sum。习惯上括号是省略的,因此 ((reduce f x) list)写作(reduce f x list)。一个三元函数如reduce,当只提供两个参数时,将成为关于那个余下参数的一元函数。一般地,对一个N元函数,提供了M(< N)个参数后,该函数便成为了关于余下的N-M个参数的函数。我们在下文中将遵守这一约定。

用这种方式将sum模块化之后,我们就可以通过对这部分的重用来收获福利了。最有趣的部分就是,reduce可以(直接)用于编写一个函数来计算列表中元素的累乘积,而不需要更多的编程步骤:

product = reduce multiply 1

它也可以用来测试一个布尔值的列表中是否至少有一个元素为真:

anytrue = reduce or false

或者它们是否都为真:

alltrue = reduce and true

理解(reduce f a)的一种方式是,将其看作一个将列表中的所有cons替换为f,将所有nil替换为a的函数。以列表[1,2,3]为例,既然它表示:

cons 1 (cons 2 (cons 3 nil))

那么(reduce add 0)将其转换为:

add 1 (add 2 (add 3 0)) = 6

(reduce multiply 1)将其转换为:

multiply 1 (mulitiply 2 (mulitiply 3 1)) = 6

于是,很明显地,(reduce cons nil)将复制列表本身。既然将一个列表追加到另一个列表上的方式是将前一个列表的元素cons到后一个列表前部,我们便得到:

append a b = reduce cons b a

例如:

append [1,2] [3,4] = reduce cons [3,4] [1,2]
= (reduce cons [3,4]) (cons 1 ( cons 2 nil ))
= cons 1 ( cons 2 [3,4]))
= [1,2,3,4] (将cons替换为cons,将nil替换为[3,4])

一个用于将列表中全部元素翻倍的函数可以写作:

doubleall = reduce doubleandcons nil
doubleandcons num list = cons ( 2*num ) list

doubleandcons可以进一步模块化,首先分解为:

doubleandcons = fandcons double
double n = 2*n
fandcons f elem list = cons (f elem) list

继续分解:

fandcons f = cons . f

其中“.”(函数复合,一个标准运算符)定义为:

(f . g) h = f(g h)

为证明fandcons的定义是正确的,我们代入一些参数:

fandcons f elem
= (cons . f) elem
= cons (f elem)

因此

fandcons f elem list = cons (f elem) list

最终得到的版本是:

doubleall = reduce (cons . double) nil

继续模块化,我们得到:

doubleall = map double
map f = reduce (cons . f) nil

其中map使任意的函数作用于列表的全部元素之上。map是另一个很通用的函数。

我们甚至可以写出一个函数累加矩阵中的所有元素,该矩阵用列表的列表表示。这个函数是:

summatrix = sum . map sum

map sum使用函数sum分别计算所有行的元素之和,而后最左边的sum将每一行的元素之和累加起来,从而得到整个矩阵的累加和。

这些例子应该已经足以使读者确信,一点模块化的努力可以产生很大的效果。通过将一个简单的函数(sum)模块化为一个“高阶函数”与一些简单参数的聚合,我们得到了一个部件(reduce),它可以用于编写与列表有关的许多函数,而又不再需要(更多的)编程努力。不止是对有关列表的函数可以这么干,举另外一个例子,考虑数据类型“有序标记树”,其定义是:

treeof X ::= node X (listof (treeof X))

这个定义表明,一棵X的树,由一个标记类型为X的结点(node),以及一个子树列表组成,而这些子树也是X的树。例如,树:

           1 o
            /\
           /  \
          /    \
        2 o     o 3
                |
                |
                |
                o 4

可以被表示成:

node 1(cons
  (node 2 nil)
  (cons (node 3
    (cons (node 4 nil) nil)
  )
nil))

我们不再给出一个函数例子并将它抽象为高阶函数,取而代之的是,直接给出一个类似于reduce的函数redtree。回忆一下,reduce有两个参数,一个用于取代cons,另一个用于取代nil。既然树由nodeconsnil组成,那么redtree必须有三个参数——用于分别取代上述三者。由于树和列表不是同一种类型,我们得定义两个函数分别处理它们。因此我们定义:

redtree f g a (node label subtrees) = f label (redtree' f g a subtrees )
redtree' f g a (cons subtree rest) = g (redtree f g a subtree) (redtree' f g a rest)
redtree' f g a nil = a

[这相当于f取代nodeg取代consa取代nil。]

很多有趣的函数都可以通过把redtree和其他函数粘起来的方法来定义。例如,要把一棵数字树上的所有标记累加起来,可以使用:

sumtree = redtree add add 0

以我们刚才表述的那棵树为例,sumtree展开成:

add 1
(add (add 2 0)
(add (add 3
(add (add 4 0) 0))
0))
= 10

要生成一个包含树中全部标记的列表,可以用:

labels = redtree cons append nil

仍然是那个例子,得到:

cons 1
(append (cons 2 nil)
(append (cons 3
(append (cons 4 nil) nil))
nil))
= [1,2,3,4]

最后,可以定义一个类似于map的函数,此函数使函数f作用于树中的全部标记上:

maptree f = redtree (node . f) cons nil

以上这些操作之所以可行,是因为函数式语言允许将传统型语言中不可分解的函数表达为一些部件的聚合,也就是一个泛化的高阶函数与一些特化函数的聚合。这样的高阶函数一旦定义,便使得很多操作都可以很容易地编写出来。不论何时,只要一个新的数据类型被定义,就应当同时定义用于处理这种数据的高阶函数。这样就简化了对数据类型的处理,同时也将与它的表示细节相关的知识局部化了。与函数式语言最相像的传统程序语言是可扩展语言,只要有需求,这种程序语言就好像随时都可以扩展出新的控制结构一样。

4.把程序粘起来

函数式语言提供的另一种黏合剂使得所有程序都可以粘在一起。回忆一下,一个完整的函数式程序只不过是一个从输入映射到输出的函数。如果fg是这样的程序,那么对程序(g.f)当提供了输入参数input之后,得到:

g (f input)

程序f计算自身的输出,此输出被用作程序g的输入。传统上,这是通过将f的输出储存在临时文件中实现的。这种方法的毛病是,临时文件可能会占用太大的空间,以至于将程序黏合起来变得很不现实。函数式语言提供了一种解决方案。程序fg严格地同步运行,只有当g试图读取输入时,f才启动,并且只运行足够的时间,恰好可以提供g需要读取的输出数据。而后f将被挂起,g将继续执行,直到它试图读取另一个输入。一个额外的好处是,如果g没有读取完f的全部输出就终止了,那么f也将被终止。f甚至可以是一个不会自行终止的程序,它可以产生无穷多的输出,因为当g运行结束时,f也将被强行终止。这就使得终止条件可以与循环体分离——一种强大的模块化形式。

这种求值方式使得f尽可能地少运行,因此被称为“惰性求值(lazy evaluation)”。它使得将程序模块化为一个产生大量可能解的生成器与一个选取恰当解的选择器的方案变得可行。有些其他的系统也允许程序以这种方式运行,但只有函数式语言对每一个函数调用都一律使用惰性求值,使得程序的每个部分都可以用这种方式模块化。惰性求值也许是函数式程序员的拿手利器中威力最大的模块化工具。

4.1.牛顿-拉夫森求根法

我们将编写一些数值算法以展现惰性求值的威力。首先,考虑用于求解平方根的牛顿-拉夫森算法。该算法从一个初始的近似值a0开始计算数N的平方根,为了求得更好的解,它使用下述规则:

a(n+1) = (a(n) + N/a(n)) / 2

如果近似值序列趋近于某一个极限a,那么

a = (a + N/a) / 2
2a = a + N/a
a = N/a
a*a = N
a = squareroot(N)

事实上,这个近似值序列确实迅速地趋近于一个极限。平方根算法取一个允许误差eps为参数,当两个相邻的近似值之差的绝对值小于eps时,算法便终止了。

这个算法通常被编写为类似下面这样:

C N IS CALLED ZN HERE SO THAT IT HAS THE RIGHT TYPE
X = A0
Y = A0 + 2.*EPS
C THE VALUE OF Y DOES NOT MATTER SO LONG AS ABS(X-Y).GT.EPS
100 IF (ABS(X-Y).LE.EPS) GOTO 200
Y = X
X = (X + ZN/X) / 2
200 CONTINUE
C THE SQUARE ROOT OF ZN IS NOW IN X

[这是一段FORTRAN的程序,C代表注释行。.LE.是“Less than or Equal to”(小于或等于)的缩写,同理.GT.是“大于”的意思。]

在传统型语言中,这个程序是不可分解的。我们将利用惰性求值将其化为更加模块化的形式,而后演示所生成部件的一些其他用途。

由于牛顿-拉夫森算法计算的是一个近似值的序列,故将它写作一个使用近似值列表的程序就再自然不过了。每个近似值都可以通过下面的函数从前一个值计算得到:

next N x = (x + N/x) / 2

因此(next N)是从一个近似值映射到下一个值的函数。调用函数f,得到近似值序列:

[a0, f a0, f(f a0), f(f(f a0)), ...]

我们可以定义一个函数来计算:

repeat f a = cons a (repeat f (f a))

因此近似值序列可以这样计算:

repeat (next N) a0

repeat是一个具有“无穷”输出的函数的例子——但这没关系,因为超出程序其余部分需求的近似值并不会被计算。无穷性只是潜在的:它只说明,只要有需求,就可以计算出任意数量的近似值,repeat本身不会强加任何限制。

求根函数的剩余部分是函数within,它取一个允许误差与一个近似值列表作为参数,并在列表中查找差值不超过允许误差的一对相邻的近似值。这个函数可以定义为:

within eps (cons a (cons b rest)) = {
= b, 如果 abs(a-b) <= eps
= within eps (cons b rest), 其他情况
}

将这两个部件结合起来,

sqrt a0 eps N = within eps (repeat (next N) a0)

现在我们得到了求根函数的两大部件,便可以尝试用不同的方式组合它们。将要进行的修改之一,是将判断条件改为“相邻近似值的比趋近1”而不是“差趋近0”。这对于非常小的数字而言更加合适(当初始的相邻近似值之间的差值很小时),对非常大的数字也是如此(当舍尾产生的误差比允许误差大很多时)。我们只需要定义一个函数来替换within,而并不需要改写生成近似值的部件:

relative eps (cons a (cons b rest)) =
= b, 如果 abs(a-b) <= eps*abs b
= relative eps (cons b rest), 其他情况

[注意:relative里的epswithin里的eps定义是不同的!前者是绝对误差后者是相对误差!]

4.2.数值微分

我们已经重用了平方根近似值序列,当然,对函数withinrelative的重用也是可能的,它们能够与任何一个生成近似值序列的数值算法配合。我们将这样来编写数值微分算法。

函数在某一点的微分,便是其图象在该点的斜率。通过分别计算函数在该点与一个临近点处的取值,而后计算两点连线斜率的方法,可以很容易地估计出微分的值。这基于一个假定:如果这两点靠得足够近,那么函数图象在两点之间不会弯曲得很厉害。于是有下述定义:

easydiff f x h = (f(x+h)-f x) / h

为了得到良好的近似值,h应该很小。不幸的是,如果h太小,那么f(x+h)f(x)会相当接近,因此在相减过程中产生的舍尾误差可能会掩盖了计算结果。如何为h选取恰当的值呢?解决这个矛盾一种合理的方案是:从一个较大取值开始,不断减小h的值,并求出一个近似值序列。这个序列将趋近于该点的导数,但最终会由于舍尾误差的存在而不可救药地变得不精确。如果我们用(within eps)来选取第一个足够精确的近似值,那么舍尾误差影响结果的风险将会大大降低。我们需要一个函数来计算这个序列:

differentiate h0 f x = map (easydiff f x) (repeat halve h0)
halve x = x/2

此处h0h的初值,而后继取值是通过不断减半得到的。通过这个函数,任意点处的导数可以这样计算:

within eps (differentiate h0 f x)

但是,甚至这个方案也不是那么令人满意的,因为近似值序列收敛得相当慢。解决这个问题需要一点数学知识,序列中的元素可以记为:

微分的精确值 + 一个关于h的误差项

理论表明,该误差项与h的某一次幂大致成正比,因此当h减小时,误差也会减小。设精确值为A,而误差项为B*h**n [**是求幂运算符]。由于计算每个近似值时所用的h取值是下一个的两倍,故任意两个连续的近似值可以表示成:

a(i)   = A + B*(2**n)*(h**n)
a(i+1) = A + B*(h**n)

现在就可以消去误差项了,我们得到:

A=(a(i+1)*(2**n) - a(i))/(2**n - 1)

当然,误差项只不过“大致”与h的某一次幂(成正比),因此这个结论也是近似的。但这是一个好得多的近似。这一改进可以通过下述函数作用于所有相邻的近似值对之上:

elimerror n (cons a (cons b rest)) =
= cons ((b*(2**n)-a)/(2**n-1)) (elimerror n (cons b rest))

从一个近似值序列中消除误差项的操作产生了另一个收敛速度快得多的序列。

使用elimerror之前还有一个问题需要解决——我们必须知道n的正确值。通常这个值很难预测,但却很容易衡量。不难验证,下述函数能够正确地消除误差项,但在此我们并不给出证明。

order (cons a (cons b (cons c rest))) =
= round(log2( (a-c)/(b-c) - 1 ))
round x = 最接近x的整数
log2 x  = x以2为底的对数

现在,一个通用的近似值序列优化函数可以定义为:

improve s = elimerror (order s) s

使用improve能够更加高效地计算函数f的导数,如下:

within eps (improve (differentiate h0 f x))

improve只对利用一个不断减半的参数h计算得到的近似值序列适用。但是,如果improve作用于这样的序列,那么其结果也是一个这样的序列!这意味着一个近似值序列可以优化不止一次。每一次优化的过程中,都有一个不同的误差项被消除,因此优化产生的序列收敛得越来越快。因此,可以非常高效地计算导数:

within eps (improve (improve (improve (differentiate h0 f x))

从数值分析的角度讲,这似乎是一个“第四阶方法”(fourth order method),可以迅速地给出准确的结果。甚至可以定义:

super s = map second (repeat improve s)
second (cons a (cons b rest)) = b

super函数使用repeat improve来生成一个不断被优化的近似值的序列的序列。[就是说,生成一个序列,其中每一个元素是一个近似值序列,而这个元素是用前一个元素优化得到的。]同时,super提取出每个近似值序列中的第二个元素,构造出一个新的序列(已经确认,第二元素是最佳选择——它比首元更精确,而且不需要额外的计算)。这个算法的确非常复杂——更多的近似值被计算的同时,它使用了不断优化的数值方法。可以用下面的程序非常非常高效地计算导数:

within eps (super (differentiate h0 f x))

这个案例可能就像是用大锤敲碎坚果一样(大材小用),但关键是,甚至一个像super一样复杂的函数,当被惰性求值的方法模块化时,也会变得很容易表达。

4.3.数值积分

在这一部分我们将讨论的最后一个例子是数值积分。问题的描述很简单:给出一个返回实数,并有一元实数参数的函数,以及两个端点ab,估算两点之间曲线f下方的面积。估算面积的最简单方法是假定f趋近于直线,此时面积就是:

easyintegrate f a b = (f a + f b)*(b-a)/2

不幸的是,除非ab足够接近,否则这个估算似乎非常不精确。更好的估算方法是,将a与b之间的区间分为两段,分别估算子区间上的面积,再将结果加起来。我们可以定义一个不断趋近于准确值的积分近似值序列,首先使用上述方程进行第一次近似,而后将分别趋近于两个子区间上的子积分准确值的(两个)近似值累加起来以得到新的(积分总体的)近似值。计算这个序列可以使用函数:

intergrate f a b = cons (easyintergrate f a b)
(map addpair (zip (intergrate f a mid)
(intergrate f mid b)))
式中 mid = (a+b)/2

zip是另一个标准的表处理函数。它读取两个列表,并返回一个有序对的列表,每个有序对由两个输入列表中对应的元素组成。从而第一对由列表一和列表二的首元组成,第二对由列表一和列表二的第二个元素组成,以此类推。zip可以定义为:

zip (cons a s) (cons b t) = cons (pair a b) (zip s t)

在函数intergrate中,zip用于生成由两个子区间上相对应的积分近似值对组成的列表,而map addpair用于将有序对中的元素相加,从而生成一个原积分的近似值列表。

实际上,这个版本的intergrate函数相当低效,因为它持续不断地重复计算f的值。就像所写的一样,easyintergrate计算了fab两处的值,而对intergrate的递归调用将重复计算它们。同样的,(f mid)也在递归调用中重复计算了。因此,更可取的是下述从不重复计算f的版本:

intergrate f a b = interg f a b (f a) (f b)
integ f a b fa fb = cons ((fa+fb)*(b-a)/2)
(map addpair (zip (interg f a m fa fm)
(interg f m b fm fb)))
式中 m = (a+b)/2
fm = f m

integrate给出了一个不断趋近准确值的积分近似值列表,正如differentiate在上一小节中所做的一样。因此可以写出计算式以求出所需任意精度的积分值,如下:

within eps (intergrate f a b)
relative eps (integrate f a b)

这个积分算法与上一小节中的第一个微分算法有着同样的缺点——它收敛得相当慢。序列中的第一个近似值仅仅用了两个相距(a-b)的点来计算(通过easyintergrate)。第二个近似值也(除了ab之外)用到了中点,因此相邻两点之间的间距仅为(b-a)/2。第三个近似值在两个子区间上作同样的处理,因此间距仅为(b-a)/4。很清楚,每个近似值对应的相邻两点之间的间距在计算下一个值时被减半了。将这一间距看作h,那么这个序列就可以成为上一小节中定义的improve函数的优化对象了。因此我们可以写出(函数来计算)快速收敛的积分近似值序列,例如:

super (intergrate sin 0 4)
improve (intergrate f 0 1)
式中 f x = 1/(1+x*x)

(后一个序列是用于计算pi/4的“第八阶方法”。其中的第二个近似值只需要计算5次f的取值,但却具有5位准确数字。)

在本节中我们选取了一些数值算法并将它们函数化地编写出来,把惰性求值当做了黏合部件的黏合剂。由于惰性求值的存在,使得我们可以用很多新的方式来模块化这些算法,从而产生用途广泛的函数,例如withinrelativeimprove。通过这些部件的不同组合,我们简单而明了地编写出了一些相当不错的数值算法。

5.人工智能中的例子

我们已经指出,函数式语言威力强大主要是因为它们提供了两种新的黏合剂:高阶函数和惰性求值。在本节中,我们将讨论人工智能中一个大一点的实例,并演示如何使用这两种黏合剂来十分简单地编写它。

我们选取的实例是alpha-beta“启发式搜索”,一个用于估计游戏者所处形势好坏的算法。该算法预测游戏局势的可能发展,但会避免对无意义局势的进一步探究。

令游戏局势使用position类型的对象来表示。这个类型依据游戏的不同而不同,我们不对此作任何假定。必然有一种方法可以知晓对某一个局势能够采取的行动:假定有一个函数:

moves: position -> listof position

该函数以一个游戏局势为参数,并返回一个可以由自变量出发,通过一步行动而形成的position的列表。以noughts and crosses游戏(tic-tac-toe)为例:

这个函数假定通过当前局势总是可以判定现在是哪位游戏者的回合。在noughts and crosses中,可以通过数出0X的数目来做到这一点。在类似于象棋的游戏中,可能必须在position类型中显式包含这一信息。

利用函数moves,第一步是构造一棵博弈树。这棵树的结点都用局势来标记,而一个结点的子结点用从该结点一步便可到达的局势标记。也就是说,如果一个结点标记为局势p,那么它的子结点将使用(moves p)中的局势来标记。一棵博弈树完全有可能是无穷的,如果这个游戏可以在双方都不胜的情形下永远进行下去的话。博弈树与第2节中讨论的树完全类似——每个结点都有一个标记(它所代表的局势)与一个子结点列表。因此我们可以使用相同的数据类型来表示它们。

博弈树是通过反复运用moves而构造出来的。构造从根局势开始,moves用于生成根结点处子树的标记,而后moves被用于生成子树的子树,依此类推。这一递归模式可以用一个高阶函数表示:

reptree f a = node a (map (reptree f) (f a))

使用这个函数可以定义另一个函数,该函数从一个特定的局势开始生成博弈树:

gametree p = reptree moves p

例如图1所示。此处使用的高阶函数reptree与上一节中用于构造无穷列表的函数repeat是类似的。

图1: 一棵博弈树的实例

alpha-beta算法从一个给定的局势出发,就游戏的发展将会是有利还是不利作出判断。然而,要做到这一点,它必须能够在不考虑下一步的情况下粗略地估计某一个局势的“价值”。在后继局势不可预测时必须使用这一函数,它也可以用来对算法进行先期引导。静态估价的结果是从计算机的角度考虑的,是对该局势的前途的度量(假设在游戏中计算机与人对抗)。结果越大,局势对计算机而言越好。结果越小,局势越糟。最简单的此类函数将会,比如说,对计算机确定胜利的局势返回+1,对计算机确定失败的局势返回-1,而对其它的局势返回0。在现实中,静态估价函数会衡量各种使局势“看上去不错”的因素。例如,具体的好处,以及象棋中对中心的控制。假定有这样一个函数:

static: position -> number

既然一棵博弈树是一个(treeof position),那么它就可以被函数(maptree static)转换为一个(treeof number),该函数对树中所有的(也许是无穷多个)局势进行静态估价。此处使用了第2节中定义的函数maptree

给出一棵静态估价树之后,其中各个局势的真值究竟是多大?特别地,对根局势应该赋予什么值?不是它的静态值,因为那只是一个粗略的猜测。一个结点被赋予的值,必须由其子结点的真值决定。这一过程的完成,基于每个游戏者都会选择对自己最有利的行动的假定。回忆一下,高值意味着计算机的有利形势。很明显,当计算机从任意的局势开始下一步行动时,它将选择通往真值最高的子结点的行动。类似地,对手将会选择通往真值最低的子结点的行动。假定计算机与其对手轮流行动,那么当轮到计算机行动时,节点的真值用函数maximise计算,反之用minimise计算。

[所谓“真值”(true value),可能是我翻译得不好,此处理解为类似“真正的价值”的意思吧,是一个量度,不是逻辑学里的0和1哦。]

maximise (node n sub) = max (map minimise sub)
minimise (node n sub) = min (map maximise sub)

此处maxmin是关于列表的函数,分别返回列表中元素的最大值与最小值。上述定义是不完整的,因为它们将永远递归下去——没有给出边界情形。我们必须定义没有后继的结点的值(其标记)。因此静态估价用于任一游戏者胜利或者后继局势不可预测的情况下。maximiseminimise的完整定义是:

maximise (node n nil) = n
maximise (node n sub) = max (map minimise sub)
maximise (node n nil) = n
maximise (node n sub) = min (map minimise sub)

在这个阶段,几乎已经可以写出一个取一个局势作为参数并返回其真值的函数了。可能是:

evaluate = maximise . maptree static . gametree

这个定义有两个问题。首先,它不适用于无穷树。maximise不断地递归直到找到一个没有子树的结点——树的端点。如果没有端点那么maximise就不会返回结果。第二个问题与第一个有关——甚至有穷的博弈树(如noughts and crosses里的那棵)事实上也可能相当大。估价整棵博弈树是不现实的——搜索必须被限定在接下去的几步之内。为此可以将树剪至一个固定的深度:

prune 0 (node a x) = node a nil
prune n (node a x) = node a (map (prune (n-1)) x)

(prune n)取一棵树作为参数并“剪去”与根结点的距离超过n的所有结点。如果一棵博弈树被剪枝,那么将强制maximise对深度为n的结点执行静态估价而不是进一步递归。因此evaluate可以被定义为:

evaluate = maximise . maptree static . prune 5 . gametree

这将考虑其后(比如说)5步的形势。

在此开发过程中我们已经使用了高阶函数与惰性求值。高阶函数reptreemaptree使得我们能够很容易地构造与处理博弈树。更重要的是,惰性求值确保了我们可以使用这种方式模块化evaluate。由于博弈树具有潜在的无穷结果,在没有惰性求值的情况下,程序将永远不会终止。我们将不能写:

prune 5 . gametree

而不得不将这两个函数整合成一个只构造树的前五层的函数。更糟糕的是,甚至那前五层都可能已经太大以至于无法在同一时间内存储于内存中。而在我们所写的程序中,函数

maptree static . prune 5 . gametree

只是构造出了树中maximise所需的部分。由于每一部分都可以在被maximise处理完之后丢弃(被垃圾收集器回收),故完整的树从来没有存储于内存中。只有树的一小部分在某一段时间内被储存着。因此这个惰性程序很有效率。这一效率取决于maximise(组合链上的最后一个函数)与gametree(第一个函数)的相互作用,因此在没有惰性求值的情况下,要完成任务,只能将组合链上的所有函数整合成一个大函数。这是对模块化的强烈破坏,但也是通常的做法。通过单独修补每个部件,我们就可以优化估价算法——这相对简单。而一个传统型程序员必须把整个程序作为一个单元来修改,这就困难多了。

到目前为止,我们只是描述了简单的对最大最小值的处理(minimaxing)。但alpha-beta算法的核心是“计算maximiseminimise的值时常常不需要考虑整棵树”这一观察结果。考虑树:

          max
          / \
         /   \
        /     \
       /       \
     min       min
     / \       / \
    /   \     /   \
   1     2   0     ?

相当奇怪地,为了估价这棵树,并不需要知道问号处的值。左子树的最小值是1,但右子树的最小值显然是一个小于或等于0的值。因此这两个最小值的最大值必然是1。这一观察结果可以被泛化并内建到maximiseminimise之中。

第一步是将maximise拆分成max对一个数字列表的作用。也就是,将maximise分解为:

maximise = max . maximise'

minimise可以用类似的方法分解。由于maximiseminimise是完全对称的,故我们将只讨论maximise,而假定minimise也照此处理。)一旦这样分解之后,maximise可以使用minimise'来发现minimise将对哪些数字求最小值,并且不再使用minimise本身。而后便可以在不查看某些数字的情况下便将它们丢弃。由于惰性求值的存在,如果maxmise并不会查看所有的数字列表,那么一部分列表将不会被计算,这是对计算机时间的潜在节约。

maxmaximise中“约分出来”是很简单的,得到:

maximise' (node n nil) = cons n nil
maximise' (node n l) = map minimise l
= map (min . minimise') l
= map min (map minimise' l)
= mapmin (map minimise' l)
式中 mapmin = map min

由于minimise'返回一个数字列表,而这个列表的最小值是minimise的结果,故(map minimise' l)返回一个数字列表的列表。Maximise'应该返回这些列表中每个列表的最小值组成的列表,但只有其中[Maximise的返回值中]的最大值才有用。我们应该定义一个mapmin的新版本以忽略那些最小值不重要的列表[在(map minimise' l)的返回值中]的最小值。

mapmin (cons nums rest) =
= cons (min nums) (omit (min nums) rest)

函数omit传递一个“潜在的最大值”——当前所发现的最小值中最大的一个——并忽略任何比该值小的最小值。

omit pot nil = nil
omit pot (cons nums rest) =
= omit pot rest, if minleq nums pot
= cons (min nums) (omit (min nums) rest), otherwise

minleq以一个数字列表和一个潜在最大值为参数,如果列表的最小值小于或等于潜在最大值就返回真。要完成这一工作,它并不需要扫描整个列表!如果列表中有任意一个元素小于或等于潜在最大值,那么列表的最小值肯定也是如此。该特别元素之后的所有元素都是无关紧要的——它们就像是上面例子中的问号一样。因此minleq可以被定义为:

minleq nil pot = false
minleq (cons num rest) port = true, if numn2

此处sort是多用途排序函数。现在估价函数定义为:

evaluate = max . maximise' . highfirst . maptree static . prune 8 . gametree

也可能认为,为了限制搜索,只要考虑计算机或者对手的前三个最佳行动也已经足够了。要编写这样的程序,只需要把highfirst换成(taketree 3 . highfirst),其中:

taketree n = redtree (nodett n) cons nil
nodett n label sub = node label (take n sub)

taketree将树上所有的结点替换为最多有n个子结点的结点,它使用了函数(take n),而该函数返回列表的前n个元素(如果列表比n短,那么返回的元素就少一些)。

另一种优化是对剪枝的改良。上述程序甚至在局势非常dynamic的情形下也会向前搜索固定的深度——(但是,)例如在国际象棋中,一旦皇后被威胁,也许就可以决定不再搜索了。通常可以定义某些“dynamic”的形势,并在遇到这样的结点之一时,不再继续搜索而停止。假定有函数“dyramic”用以确定这样的形势,那么只需要为prune追加一个定义等式:

prune 0 (node pos sub) = node pos (map (prune 0) sub), if dynamic pos

在像这个程序一样模块化的程序里,作出这样的改动是很简单的。如前所述,这个程序的效率,关键是由链中的最后一个函数maximise与第一个函数gametree的相互作用决定的,因此若没有惰性求值,就只能写成一个单一的程序。这样的程序难于编写,难于修改,而且,非常难于理解。

6.结论

在本文中,我们指出模块化是成功的程序设计的关键。以提高生产力为目标的程序语言,必须良好地支持模块化程序设计。但是,新的作用域规则和分块编译的技巧是不够的——“模块化”不仅仅意味着“模块”。我们分解程序的能力直接取决于将解决方案粘在一起的能力。为了协助模块化程序设计,程序语言必须提供优良的黏合剂。函数式程序语言提供了两种新的黏合剂——高阶函数惰性求值。利用这些黏合剂可以将程序用新的、令人激动的方式模块化,对此我们举出了很多实例。越小、越通用的模块越可能被广泛地重用,使后续的程序设计工作变得简单。这解释了为什么函数式程序与传统型程序比较,要小得多,也容易编写得多。它也为函数式程序员提供了一个追求目标。如果程序的任何部分是杂乱或者复杂的,那么程序员就应当尝试将其模块化并泛化其部件。他应当期望把高阶函数和惰性求值用作他做此事的工具。

当然,我们并不是指出高阶函数与惰性求值的力与美的第一人。例如,Turner展示了这两者如何在一个生成化学结构的程序里大显身手[Tur81]。Abelson和Sussman强调“流”(惰性列表)是构架程序的强大工具[AS86]。Henderson使用了流来构架函数式操作系统[P.H82]。本论文的主要贡献是,断言了模块化自身,便是函数式语言强大威力的关键。

这与当前有关惰性求值的论战也有关联。有些人认为函数式语言应当是惰性的,而其他人认为不是这样。有些人走折衷路线,只提供惰性列表以及用于构造它们的特殊语法(例如,在SCHEME中[AS86])。本论文提供了更进一步的证据,证明惰性求值非常重要以至于不能被降为二等公民。这也许是函数式程序员所拥有的最强大的黏合剂。人们不应当阻碍对这样一个极为重要的工具的使用。

致谢

在牛津程序设计研究组与Phil Wadler和Richard Bird的多次交谈对本论文的写作帮助甚大。约特堡查麦兹大学的Magnus Bondesson指出了一个数值算法的早期版本中的严重错误,同时也协助了很多其他算法的开发。本论文在英国科学与工程研究评议会提供的研究基金赞助下发表。

参考文献

  • [AS86] H.Abelson,G.J.Sussman. 计算机程序的构造与解释. 麻省理工学院出版社,波士顿,1986
  • [Hug89] J.Hughes. 函数式程序设计为什么至关重要. 计算机月刊,32(2),1989
  • [Hug90] John Hughes. 函数式程序设计为什么至关重要. D.Turner主编,函数式编程的研究主题. Addison Wesley,1990
  • [MTH90] R.Milner,M.Tofte,R.Harper. Standard ML的定义. 麻省理工学院出版社,1990
  • [oD80] 美利坚合众国国防部. 程序语言Ada参考手册. Springer-Verlag,1980
  • [P.H82] P.Henderson. 纯函数式操作系统. 1982
  • [Tur81] D.A.Turner. 应用语言在语义上的优雅性. 1981年度函数式语言与计算机架构会议会报,海边温特渥,普茨茅斯,新汉普夏郡,1981
  • [Tur85] D.A.Turner. Miranda: 拥有多态类型的非严格语言. 1985年度函数式语言与计算机架构会议会报,1-16页,南锡,法国,1985
  • [Wir82] N.Wirth. Modula-II程序设计. Springer-Verlag,1982

HTTP协议头部与Keep-Alive模式详解

1、什么是Keep-Alive模式?

我们知道HTTP协议采用“请求-应答”模式,当使用普通模式,即非KeepAlive模式时,每个请求/应答客户和服务器都要新建一个连接,完成 之后立即断开连接(HTTP协议为无连接的协议);当使用Keep-Alive模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服 务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。

http 1.0中默认是关闭的,需要在http头加入"Connection: Keep-Alive",才能启用Keep-Alive;http 1.1中默认启用Keep-Alive,如果加入"Connection: close ",才关闭。目前大部分浏览器都是用http1.1协议,也就是说默认都会发起Keep-Alive的连接请求了,所以是否能完成一个完整的Keep- Alive连接就看服务器设置情况。

2、启用Keep-Alive的优点

从上面的分析来看,启用Keep-Alive模式肯定更高效,性能更高。因为避免了建立/释放连接的开销。下面是RFC 2616 上的总结:

    1. By opening and closing fewer TCP connections, CPU time is saved in routers and hosts (clients, servers, proxies, gateways, tunnels, or caches), and memory used for TCP protocol control blocks can be saved in hosts.
    2. HTTP requests and responses can be pipelined on a connection. Pipelining allows a client to make multiple requests without waiting for each response, allowing a single TCP connection to be used much more efficiently, with much lower elapsed time.
    3. Network congestion is reduced by reducing the number of packets caused by TCP opens, and by allowing TCP sufficient time to determine the congestion state of the network.
    4. Latency on subsequent requests is reduced since there is no time spent in TCP's connection opening handshake.
    5. HTTP can evolve more gracefully, since errors can be reported without the penalty of closing the TCP connection. Clients using future versions of HTTP might optimistically try a new feature, but if communicating with an older server, retry with old semantics after an error is reported.
RFC 2616 (P47)还指出:单用户客户端与任何服务器或代理之间的连接数不应该超过2个。一个代理与其它服务器或代码之间应该使用不超过2 * N的活跃并发连接。这是为了提高HTTP响应时间,避免拥塞(冗余的连接并不能代码执行性能的提升)。

3、回到我们的问题(即如何判断消息内容/长度的大小?)

Keep-Alive模式,客户端如何判断请求所得到的响应数据已经接收完成(或者说如何知道服务器已经发生完了数据)?我们已经知道 了,Keep-Alive模式发送玩数据HTTP服务器不会自动断开连接,所有不能再使用返回EOF(-1)来判断(当然你一定要这样使用也没有办法,可 以想象那效率是何等的低)!下面我介绍两种来判断方法。

3.1、使用消息首部字段Conent-Length

故名思意,Conent-Length表示实体内容长度,客户端(服务器)可以根据这个值来判断数据是否接收完成。但是如果消息中没有Conent-Length,那该如何来判断呢?又在什么情况下会没有Conent-Length呢?请继续往下看……

3.2、使用消息首部字段Transfer-Encoding

当客户端向服务器请求一个静态页面或者一张图片时,服务器可以很清楚的知道内容大小,然后通过Content-length消息首部字段告诉客户端 需要接收多少数据。但是如果是动态页面等时,服务器是不可能预先知道内容大小,这时就可以使用Transfer-Encoding:chunk模式来传输 数据了。即如果要一边产生数据,一边发给客户端,服务器就需要使用"Transfer-Encoding: chunked"这样的方式来代替Content-Length。

chunk编码将数据分成一块一块的发生。Chunked编码将使用若干个Chunk串连而成,由一个标明长度为0 的chunk标示结束。每个Chunk分为头部和正文两部分,头部内容指定正文的字符总数(十六进制的数字 )和数量单位(一般不写),正文部分就是指定长度的实际内容,两部分之间用回车换行(CRLF) 隔开。在最后一个长度为0的Chunk中的内容是称为footer的内容,是一些附加的Header信息(通常可以直接忽略)。 Chunk编码的格式如下:

Chunked-Body = *<strong>chunk </strong>
"0" CRLF
footer
CRLF
chunk = chunk-size [ chunk-ext ] CRLF
chunk-data CRLF

hex-no-zero = &lt;HEX excluding "0"&gt;

chunk-size = hex-no-zero *HEX
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-value ] )
chunk-ext-name = token
chunk-ext-val = token | quoted-string
chunk-data = chunk-size(OCTET)

footer = *entity-header

即Chunk编码由四部分组成: 1、<strong>0至多个chunk块</strong> ,2、<strong>"0" CRLF </strong>,3、<strong>footer </strong>,4、<strong>CRLF</strong> <strong>.</strong> 而每个chunk块由:chunk-size、chunk-ext(可选)、CRLF、chunk-data、CRLF组成。

4、消息长度的总结

其实,上面2中方法都可以归纳为是如何判断http消息的大小、消息的数量。RFC 2616 对 消息的长度总结如下:一个消息的transfer-length(传输长度)是指消息中的message-body(消息体)的长度。当应用了 transfer-coding(传输编码),每个消息中的message-body(消息体)的长度(transfer-length)由以下几种情况 决定(优先级由高到低):

  • 任何不含有消息体的消息(如1XXX、204、304等响应消息和任何头(HEAD,首部)请求的响应消息),总是由一个空行(CLRF)结束。
  • 如果出现了Transfer-Encoding头字段 并且值为非“identity”,那么transfer-length由“chunked” 传输编码定义,除非消息由于关闭连接而终止。
  • 如果出现了Content-Length头字段,它的值表示entity-length(实体长度)和transfer-length(传输长 度)。如果这两个长度的大小不一样(i.e.设置了Transfer-Encoding头字段),那么将不能发送Content-Length头字段。并 且如果同时收到了Transfer-Encoding字段和Content-Length头字段,那么必须忽略Content-Length字段。
  • 如果消息使用媒体类型“multipart/byteranges”,并且transfer-length 没有另外指定,那么这种自定界(self-delimiting)媒体类型定义transfer-length 。除非发送者知道接收者能够解析该类型,否则不能使用该类型。
  • 由服务器关闭连接确定消息长度。(注意:关闭连接不能用于确定请求消息的结束,因为服务器不能再发响应消息给客户端了。)

为了兼容HTTP/1.0应用程序,HTTP/1.1的请求消息体中必须包含一个合法的Content-Length头字段,除非知道服务器兼容 HTTP/1.1。一个请求包含消息体,并且Content-Length字段没有给定,如果不能判断消息的长度,服务器应该用用400 (bad request) 来响应;或者服务器坚持希望收到一个合法的Content-Length字段,用 411 (length required)来响应。

所有HTTP/1.1的接收者应用程序必须接受“chunked” transfer-coding (传输编码),因此当不能事先知道消息的长度,允许使用这种机制来传输消息。消息不应该够同时包含 Content-Length头字段和non-identity transfer-coding。如果一个消息同时包含non-identity transfer-coding和Content-Length ,必须忽略Content-Length 。

5、HTTP头字段总结

最后我总结下HTTP协议的头部字段。

  • 1、 Accept:告诉WEB服务器自己接受什么介质类型,/ 表示任何类型,type/* 表示该类型下的所有子类型,type/sub-type。
  • 2、 Accept-Charset: 浏览器申明自己接收的字符集 Accept-Encoding: 浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate) Accept-Language:浏览器申明自己接收的语言 语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等等。
  • 3、 Accept-Ranges:WEB服务器表明自己是否接受获取其某个实体的一部分(比如文件的一部分)的请求。bytes:表示接受,none:表示不接受。
  • 4、 Age:当代理服务器用自己缓存的实体去响应请求时,用该头部表明该实体从产生到现在经过多长时间了。
  • 5、 Authorization:当客户端接收到来自WEB服务器的 WWW-Authenticate 响应时,用该头部来回应自己的身份验证信息给WEB服务器。
  • 6、 Cache-Control:请求:no-cache(不要缓存的实体,要求现在从WEB服务器去取) max-age:(只接受 Age 值小于 max-age 值,并且没有过期的对象) max-stale:(可以接受过去的对象,但是过期时间必须小于 max-stale 值) min-fresh:(接受其新鲜生命期大于其当前 Age 跟 min-fresh 值之和的缓存对象) 响应:public(可以用 Cached 内容回应任何用户) private(只能用缓存内容回应先前请求该内容的那个用户) no-cache(可以缓存,但是只有在跟WEB服务器验证了其有效后,才能返回给客户端) max-age:(本响应包含的对象的过期时间) ALL: no-store(不允许缓存)
  • 7、 Connection:请求:close(告诉WEB服务器或者代理服务器,在完成本次请求的响应后,断开连接,不要等待本次连接的后续请求了)。 keepalive(告诉WEB服务器或者代理服务器,在完成本次请求的响应后,保持连接,等待本次连接的后续请求)。 响应:close(连接已经关闭)。 keepalive(连接保持着,在等待本次连接的后续请求)。 Keep-Alive:如果浏览器请求保持连接,则该头部表明希望 WEB 服务器保持连接多长时间(秒)。例如:Keep-Alive:300
  • 8、 Content-Encoding:WEB服务器表明自己使用了什么压缩方法(gzip,deflate)压缩响应中的对象。例如:Content-Encoding:gzip
  • 9、Content-Language:WEB 服务器告诉浏览器自己响应的对象的语言。
  • 10、Content-Length: WEB 服务器告诉浏览器自己响应的对象的长度。例如:Content-Length: 26012
  • 11、Content-Range: WEB 服务器表明该响应包含的部分对象为整个对象的哪个部分。例如:Content-Range: bytes 21010-47021/47022
  • 12、Content-Type: WEB 服务器告诉浏览器自己响应的对象的类型。例如:Content-Type:application/xml
  • 13、ETag:就是一个对象(比如URL)的标志值,就一个对象而言,比如一个 html 文件,如果被修改了,其 Etag 也会别修改,所以ETag 的作用跟 Last-Modified 的作用差不多,主要供 WEB 服务器判断一个对象是否改变了。比如前一次请求某个 html 文件时,获得了其 ETag,当这次又请求这个文件时,浏览器就会把先前获得的 ETag 值发送给WEB 服务器,然后 WEB 服务器会把这个 ETag 跟该文件的当前 ETag 进行对比,然后就知道这个文件有没有改变了。
  • 14、 Expired:WEB服务器表明该实体将在什么时候过期,对于过期了的对象,只有在跟WEB服务器验证了其有效性后,才能用来响应客户请求。是 HTTP/1.0 的头部。例如:Expires:Sat, 23 May 2009 10:02:12 GMT
  • 15、 Host:客户端指定自己想访问的WEB服务器的域名/IP 地址和端口号。例如:Host:rss.sina.com.cn
  • 16、 If-Match:如果对象的 ETag 没有改变,其实也就意味著对象没有改变,才执行请求的动作。
  • 17、 If-None-Match:如果对象的 ETag 改变了,其实也就意味著对象也改变了,才执行请求的动作。
  • 18、 If-Modified-Since:如果请求的对象在该头部指定的时间之后修改了,才执行请求的动作(比如返回对象),否则返回代码304,告诉浏览器 该对象没有修改。例如:If-Modified-Since:Thu, 10 Apr 2008 09:14:42 GMT
  • 19、 If-Unmodified-Since:如果请求的对象在该头部指定的时间之后没修改过,才执行请求的动作(比如返回对象)。
  • 20、 If-Range:浏览器告诉 WEB 服务器,如果我请求的对象没有改变,就把我缺少的部分给我,如果对象改变了,就把整个对象给我。浏览器通过发送请求对象的 ETag 或者 自己所知道的最后修改时间给 WEB 服务器,让其判断对象是否改变了。总是跟 Range 头部一起使用。
  • 21、 Last-Modified:WEB 服务器认为对象的最后修改时间,比如文件的最后修改时间,动态页面的最后产生时间等等。例如:Last-Modified:Tue, 06 May 2008 02:42:43 GMT
  • 22、 Location:WEB 服务器告诉浏览器,试图访问的对象已经被移到别的位置了,到该头部指定的位置去取。例如:Location:http://i0.sinaimg.cn/dy/deco/2008/0528/sinahome_0803_ws_005_text_0.gif
  • 23、 Pramga:主要使用 Pramga: no-cache,相当于 Cache-Control: no-cache。例如:Pragma:no-cache
  • 24、 Proxy-Authenticate: 代理服务器响应浏览器,要求其提供代理身份验证信息。Proxy-Authorization:浏览器响应代理服务器的身份验证请求,提供自己的身份信息。
  • 25、 Range:浏览器(比如 Flashget 多线程下载时)告诉 WEB 服务器自己想取对象的哪部分。例如:Range: bytes=1173546-
  • 26、 Referer:浏览器向 WEB 服务器表明自己是从哪个 网页/URL 获得/点击 当前请求中的网址/URL。例如:Referer:http://www.sina.com/
  • 27、 Server: WEB 服务器表明自己是什么软件及版本等信息。例如:Server:Apache/2.0.61 (Unix)
  • 28、 User-Agent: 浏览器表明自己的身份(是哪种浏览器)。例如:User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.14) Gecko/20080404 Firefox/2、0、0、14
  • 29、 Transfer-Encoding: WEB 服务器表明自己对本响应消息体(不是消息体里面的对象)作了怎样的编码,比如是否分块(chunked)。例如:Transfer-Encoding: chunked
  • 30、 Vary: WEB服务器用该头部的内容告诉 Cache 服务器,在什么条件下才能用本响应所返回的对象响应后续的请求。假如源WEB服务器在接到第一个请求消息时,其响应消息的头部为:Content- Encoding: gzip; Vary: Content-Encoding那么 Cache 服务器会分析后续请求消息的头部,检查其 Accept-Encoding,是否跟先前响应的 Vary 头部值一致,即是否使用相同的内容编码方法,这样就可以防止 Cache 服务器用自己 Cache 里面压缩后的实体响应给不具备解压能力的浏览器。例如:Vary:Accept-Encoding
  • 31、 Via: 列出从客户端到 OCS 或者相反方向的响应经过了哪些代理服务器,他们用什么协议(和版本)发送的请求。当客户端请求到达第一个代理服务器时,该服务器会在自己发出的请求里面添 加 Via 头部,并填上自己的相关信息,当下一个代理服务器收到第一个代理服务器的请求时,会在自己发出的请求里面复制前一个代理服务器的请求的Via 头部,并把自己的相关信息加到后面,以此类推,当 OCS 收到最后一个代理服务器的请求时,检查 Via 头部,就知道该请求所经过的路由。例如:Via:1.0 236.D0707195.sina.com.cn:80 (squid/2.6.STABLE13)

=============================================================================== HTTP 请求消息头部实例: Host:rss.sina.com.cn User-Agent:Mozilla/5、0 (Windows; U; Windows NT 5、1; zh-CN; rv:1、8、1、14) Gecko/20080404 Firefox/2、0、0、14 Accept:text/xml,application/xml,application/xhtml+xml,text/html;q=0、9,text/plain;q=0、8,image/png,/;q=0、5 Accept-Language:zh-cn,zh;q=0、5 Accept-Encoding:gzip,deflate Accept-Charset:gb2312,utf-8;q=0、7,*;q=0、7 Keep-Alive:300 Connection:keep-alive Cookie:userId=C5bYpXrimdmsiQmsBPnE1Vn8ZQmdWSm3WRlEB3vRwTnRtW &lt;-- Cookie If-Modified-Since:Sun, 01 Jun 2008 12:05:30 GMT Cache-Control:max-age=0 HTTP 响应消息头部实例: Status:OK - 200 -- 响应状态码,表示 web 服务器处理的结果。 Date:Sun, 01 Jun 2008 12:35:47 GMT Server:Apache/2.0.61 (Unix) Last-Modified:Sun, 01 Jun 2008 12:35:30 GMT Accept-Ranges:bytes Content-Length:18616 Cache-Control:max-age=120 Expires:Sun, 01 Jun 2008 12:37:47 GMT Content-Type:application/xml Age:2 X-Cache:HIT from 236-41.D07071951.sina.com.cn -- 反向代理服务器使用的 HTTP 头部 Via:1.0 236-41.D07071951.sina.com.cn:80 (squid/2.6.STABLE13) Connection:close

轉自 http://a280606790.iteye.com/blog/1095085

山有木兮木有枝,心悅君兮君不知

今天在網上偶然發現了一首怪詩,曰:「濫兮抃草濫予昌枑澤予昌州州𩜱州焉乎秦胥胥縵予乎昭澶秦踰滲惿隨河湖」。讀後百思不得其解,一個字句也沒看懂。當看到另一個版本時,才發現這是一首優美的古代歌謠:

今夕何夕兮,搴舟中流。
今日何日兮,得與王子同舟。
蒙羞被好兮,不訾詬恥。
心幾煩而不絕兮,得知王子。
山有木兮木有枝,心悅君兮君不知。

這是一首古越人的歌謠,用漢字記錄其語音,錄於在漢代劉向《說苑》中,上面是漢語翻譯的文字。不得不佩服古人,能把一首異族的歌謠翻譯地這麼有韻味。這首歌謠可謂是不可多得的上古漢語對音文獻,對上古漢語語音研究有不可多得的意義。同時要透徹地研究這首歌謠,必須瞭解上古漢語的語音系統。隨即又在鄭張尚芳先生的博客上發現了關於這篇歌謠的研究,看過之後如醍醐灌頂,故轉載於此。

以下轉自鄭張尚芳博客

最近發現《越人歌》突然在網上紅了起來,原來是電影《夜宴》拿《越人歌》作了插曲。周迅娓娓的唱腔打動了很多人。《夜宴》捧紅了《越人歌》,但卻又把《越人歌》安錯了家門。《夜宴》的對外宣傳都把《越人歌》說成是根據《詩經》填的詞,這真是大錯特錯。不知道這是爲了提高自己插曲的地位還是壓根兒就真不知道《越人歌》的出處?《越人歌》其實著錄於漢代劉向《說苑》卷十一《善說篇》,跟《詩經》完全沒有關係,它是公元前528年,拿槳的越人爲遊湖的楚國王子鄂君子皙唱的歌。《越人歌》雖然走紅了,但知道《越人歌》隱藏了一個千古之謎的人還真不多。我們上面看到的只是楚人將《越人歌》翻譯成楚辭形式的漢語翻譯。《越人歌》是《越人擁楫歌》的簡稱,是古代越人所唱的歌,越人原來肯定不是這麼唱的。《善說篇》中還用漢字記錄了它的古越語發音:

濫兮抃草濫
予昌枑澤、予昌州
州𩜱州焉乎、秦胥胥
縵予乎、昭澶秦踰
滲惿隨河湖

《越人歌》原文用漢字記音有三十二字,而楚譯人把它譯成楚辭的形式後,用了五十四個字,竟多了二十二字;可見兩者不是一種語言,所以不能字字對譯。因爲雙方歌式也不同,楚譯人爲了使譯文合於楚辭歌式,其中還包含有一些只爲湊韻而添加的起興式遊辭。所以這一段如同天書的漢字記音也就成了一個千古之謎,很多人都在猜測它的原義。破譯《越人歌》實際上有幾個難題。首先要解決的是用來記音的漢字在當時的發音,也是就漢字的上古音。其次是每個字的上古音對應古越語的什麼意思。最後,串聯成句的翻譯應當與《善說篇》的楚辭翻譯能對應得上。由於民族學者推測古越族屬南島語族或侗台語族,1953年日本學者泉井久之助將《越人歌》試用占語進行對比,1981年韋慶穩教授提出與僮語比較,做了很多嘗試,但都存在很多問題。

1991年,我的《越人歌的解讀》以英文發表於法國高等社會科學院《東方語言學報》(CLOA)22卷2號,後來經孫琳、石鋒翻譯成漢語發表於《語言研究論叢》(語文出版社1997)。我把漢字依古音用侗台語裏文字形式較古的泰文爲主進行譯解,分原文爲五句:

濫兮抃草濫         夜晚哎、歡樂相會的夜晚,
予昌枑澤、予昌州      我好害羞,我善搖船,
州𩜱 州焉乎、秦胥胥  搖船渡越、搖船悠悠啊,高興喜歡!
縵予乎、昭澶秦踰      鄙陋的我啊、王子殿下竟高興結識,
滲惿隨河湖         隱藏心裏在不斷思戀哪!

這個譯解用的是我的古音擬音系統,所對泰文是個內部統一的音韻系統,對音條例很規則,只在個別音類出現變異的例子才引證同語族語言中同樣變異的語例。對原譯四層意思尤其是「蒙羞被好」、「心幾頑而不絕兮得知王子」(自羞鄙陋而王子不以此見絕)的感激之情都有了相對應的交代。(只有「山有木兮木有枝」一句當是楚國譯人爲滿足楚辭韻例湊足六句而添加的襯韻句,以「枝」諧「知」而已,故泉井氏、韋氏也都沒有把此句考慮在內)。我的對譯得到我國台語研究大師邢公畹先生及國外兩位泰文專家的肯定,由此可以確定越人歌是用一種古台語寫作的。

下面我們來舉例說明一下我的對譯。看不懂音標也沒關係,主要是瞭解一個過程。如:

第一句:[濫兮抃草濫]——夜晚哎、歡樂相會的夜晚

濫,古音Hgraamh:泰文g1am’夜晚,黑暗
兮,古音Hee:泰文 Hee哎
抃,古音brons:泰文blxxn歡欣、陶醉(x代後高半元音,相當拼音 e)
草,古音tshuu:泰文cx’,遇見、相會
濫,古音Hgraamh:泰文g1am’夜晚

下面我們進一步省略音標來說明。

第二句:[予昌枑澤、予昌州]—— 我好害羞,我善搖船

予la:泰文ra我們,我
昌thjaang:泰文djaangh很會、多麼
枑澤gaah-draag:泰文kra’-’daak害羞,難爲情
州tju:泰文ceeu搖船

第三句:[州𩜱 州焉乎、秦胥胥]——搖船渡越、搖船悠悠啊,高興喜歡

州:搖船
𩜱 khaam’:泰文khaam’渡越
州:搖船
焉jen:泰文jxxnh久久
乎Ha:泰文Ha啊
秦dzin:泰文djnh愉快
胥胥sa:泰文sa’滿意、稱心

第四句:[縵予乎、昭澶秦踰]—— 鄙陋的我啊、王子殿下竟高興結識

縵moonh:泰文moom污穢
予:我
乎:啊
昭tjau:泰文cau’王子,主、君
澶daanh:泰文daanh閣下
秦:高興的
踰lo:泰文ruu’知悉、曉

第五句:[滲惿隨河湖]——隱藏心裏在不斷思戀哪

滲sr mh:泰文zumh隱藏
惿dje’:泰文ca 心
隨ljoi:泰文raih始終不斷
河gaai:泰文graih思慕
湖gaa:泰文ga’ 哇[語助詞]

可以注意的是原語中有許多與漢語是同源的:「兮、乎、予」幾乎都同音同義。「州」也就是「舟」,但用爲動詞。「踰」也就是「喻」 [家喻戶曉的喻]。「昭:主」、「抃:忭」、「草:遭」、「昌;匠」、「秦;親」、「惿:志」、「澶:殿」、「濫:暗陰」等分別音義相關。譯「心」專選一個心旁的罕用字「惿」,也似乎有點特別用心。以「昭」對「主」也是後世「詔」對「主」的先聲。從此歌譯解看,既證明越人操一種侗台語,又證明它的許多語詞也是與漢語同根的。

此一譯文在韻律上是二與四句、三與五句各自相叶,第一句是「濫」字首尾循環.「草」叶第二第四句(「草」古幽部,與「州」叶、侯部「踰」亦韻近)。本譯文雖與韋譯同用台語比較,但因古音擬音見解不同,故只「濫」字的夜晚義同韋氏。

泰文 raa 表「我們倆、我」,同源的呂語 hra、白泰語 ha則都表「我」(李方桂1977)。

泰文 sa「稱心」對「胥」字,此詞也見於「姑胥」即吳王在其都城郊外山間的夏宮之名(也譯爲「姑蘇」),同樣有「稱心之地」的含義。「秦胥胥」跟「州{飠甚 }州焉」一樣,是台語裏常見的、使用重疊手法的詞語修辭變化方式。

根據以上的解讀,最後將《越人歌》全文今譯與古譯對照如下:

濫兮抃草濫(夜晚哎、歡樂相會夜晚)
今夕何夕兮,

予昌枑澤、予昌州(我多害羞,我多能搖船)
(蒙羞被好)搴舟中流

州𩜱州焉乎、秦胥胥(搖船渡越、搖船悠悠啊,高興喜歡)
今日何日兮,得與王子同舟

縵予乎、昭澶秦踰(鄙陋的我啊、蒙王子殿下高興結識)
蒙羞被好兮不訾詬恥。心幾頑而不絕兮,得知王子。

滲惿隨河湖(隱藏心裏在不斷思戀)
山有木兮木有枝,心悅君兮君不知

精英的「灰飛煙滅」與大衆的「浴火重生」——從清華學堂失火看精英與大衆的分裂

BYVoid註:本文轉載於自網絡,作者係北大2008級本科生江東晛。我將之重新排版。

本文邏輯結構

清华学堂火灾——网民的非理性反应
第一部分:同情心的缺失——反映出精英与大众的严重对立、人心的扭曲
第二部分:从精英的角度看大众——平民意识确实在无理取闹
第三部分:从大众的角度看精英——中国精英的人才流失——教育特权化的社会意识——受过教育的精英脱离了大众,坠入了自我利益的深渊——大众不是完全在无理取闹
第四部分:知识精英的命运——超阶级性作为知识阶层的特质、知识阶层“设计社会”的使命——当今中国知识精英的阶级化危险——历史:新文化运动之后独立知识分子的悲剧——现实:中国知识精英可能会重复阶级化的悲剧
第五部分:社会极化的逻辑——精英的寡头化导致下层的民粹化——意识形态重新武装社会
第六部分:理想社会的期待——正常社会中精英与大众的制约关系——知识精英的最后防线——中国精英的普遍使命

正文始

2010年11月13日,就在清华大学即将迎来百年校庆之际,一把大火却没有任何预兆地降临在了这所学校的头上。该校历史最悠久、意义最重大的建筑——清华学堂,在这场大火中遭遇了重创。虽然在外观上看不出来主体结构有任何损坏,但是,当两眼蔚蓝色的天空透过空洞的窗棂呈现在我眼前的时候,我知道,里面的木结构材料早已经灰飞烟灭了。

我明白这对于一座建于上世纪初的建筑意味着什么。南开中学的校史馆——伯苓楼,那栋跟清华学堂相差仅几岁的建筑,我有机会出入过很多次。古老的木楼梯、木地板在你的脚下嘎嘎作响,似乎那声音是要跟你讲述一段段故事。它可能会告诉你,周恩来当年喜欢在哪个窗户向外眺望那盛开的西府海棠,张伯苓当年在哪个房间里用一口浓重的天津话跟杰出的学生促膝长谈。同样的道理也适用于清华学堂——那曾经是四大导师授课的地方,到现在都是中国文化精神的象征。它的对面不远处就是“海宁王静安先生纪念碑”,“独立之精神,自由之思想”这十字箴言就镌刻在上面。现在,静安先生的碑还在,而他在学堂里的足迹真的是灰飞烟灭了。难怪有清华学生在人人网上说,起火的那天晚上,看到一位老教授样子的人,推着自行车,在寒冷但明亮的夜幕中痴痴地站着,仅仅穿着单衣,一句话也不说。因为他明白学堂对清华的意义。

正是在这种文化建构的意义上,一位留学海外的清华学子特意用繁体字书写了自己的感叹: “學堂對每一個清華人來講,意義自然遠非一座建築、一個國家級保護文物那麼簡單,她是一種自由獨立的精神,一段厚重的歷史,是清華的百年滄桑,甚至是那個時代的人文之魂,是每個清華人心中不可侵犯的神聖。尤其對我這樣一個遠在重洋的遊子而言,學堂此番蒙難,就像是家園被毀,瞬時沒有了歸屬感。我也怨為何不天降甘霖,我甚至悔恨我此時不在她身邊,無由睹其憔悴容顏。”[1]

于是我们就看到了从一个学堂的失火而导引出的社会文化意识:在清华学生看来,学堂的劫难,其实是“自由独立精神”的灰飞烟灭,甚至是理想的精英人格的最后一点象征符号的灰飞烟灭。

一、同情心的丢失:精英与大众对立的表象

然而,我这篇文章讨论的重点并不在学堂失火事件本身。一个偶然的机会,我在网易新闻上注意到了网友们的评论。那其实已经远远超出“评论”的范畴了,而简直就是一场争吵。我不知道争吵的由来,映入眼帘的首先是清华大学某学生在这场争吵中无奈的言辞: 唉!随便你们怎么说吧,清华人选择沉默。烧的不是你们家,你们不知道悲伤。中国人就是这个样,能有什么办法?我们只是觉得委屈,难道我们一天天努力地跟狗一样,为了就是振兴你们这群人的中华吗?我们有99%的就业率,但我们还是高分低能。各大工程都有清华人的身影,但我们还是对不起社会……没有办法,纳税人说什么就是什么吧!你们纳了点的税款,就对社会做出了比两弹一星大了无数倍的贡献。反正社会上清华人少,非清华人多。清华人所能做的,只有沉默。行胜于言,你们再说也改变不了什么,清华,永远是清华!

我能够看出来这是怎么一回事了。肯定是在报道清华学堂失火的这则新闻下面,网友的留言根本没有表现出任何惋惜、心痛或者同情之情,相反,是一系列的冷嘲热讽甚至谩骂。清华学生在后面发的一篇总结性的长文证明了这一点: 在我发这篇帖子的时候,这篇新闻下边的评论主要还不是对清华本身的批评,也不是对资源分配的不满,而是很多不分青红皂白的、对清华学堂着火事件的谩骂、羞辱、嘲笑。后来的很多评论说拆迁、批清华都是对社会、对清华的一些不满和批判,其实并没有针对清华本身,而是针对社会上的种种不合理现象,这种评论,无论是否尖刻,都是我们可以接受的。但是,在最开始,母校遭受严重打击的清华人,看到的只有幸灾乐祸,只有落井下石。我不知道这到底是一种什么心理,但可以确认的是这不是后来一些有理性网友的“爱之深责之切”的心态。而面对这样一种对别人的苦难看热闹的心态,我发言了,针对的是那些单纯为清华失火而高兴、围观的人。

我们所不能接受的,只是对于别人苦难的视若无睹,甚至还要大声叫好的人。

……

你们可以批评清华的种种不是,但请不要对失火的学堂拍手称快,这是两回事。

清华的学生们对于这种现象感到不理解。在构思写这篇文章的时候,我也一直在思考这个问题。母校的象征性建筑被毁,清华学生感到痛惜,这是一种很自然的情感——因为烧的是“我”关心的东西。然而,这种情感是否在人性中具有共通性?现在已经是一个不相信人类思想意识具有共通性的时代了,所以我们必须要对这个问题进行论证。正像我们在上文提到的那样,清华学生对学堂被毁的痛惜,在一种“文化建构”的意义上被表达了出来。学堂不被当作一座单纯的“建筑”或“文物保护单位”,而是一种的“精神”,一段“歷史”,是“百年滄桑”,是“人文之魂”,是“不可侵犯的神聖”。这种文化建构在清华人的心中是起作用的,在理解清华精神并且对中国历史文化感兴趣的所有“知识分子”当中也是起作用的。正因为这个原因,北大人才会在那个乱糟糟的论坛中与“对门”的学生站在一起,共同守护学堂的尊严。但是,这种在小众范围内的文化建构,是否能够为大众所认同?在大多数人的心中,也许清华仅仅是一个高分学生集中的地方,他们可能不了解清华的历史,因此也不理解清华学堂在中国教育史甚至是现代文化史上的地位。那么,学堂被毁是不是就无法在他们的心中产生剧烈的影响?这个答案是肯定的,但接下来的问题是,无法产生像清华学生热爱母校那样强烈的情感,是否就意味着一定会产生与之完全相反的情感?比如“谩骂、羞辱、嘲笑”?比如“对于别人苦难的视若无睹,甚至还要大声叫好”?比如“对失火的学堂拍手称快”?要回答这个问题,我们必须抛开对清华学堂文化意义的建构和阐释,而对之进行解构,并且要一直解构到清华学堂的“自然状态”——那就是一个房子而已,是国家的财产,是个历史风貌建筑。从纯粹经济学的角度看,房子意味着财产,而房子被毁就意味着财产的损失。如果我们告诉一个普通人说,圆明园旁边有个房子着火了,我想那个普通人的第一反应应该是问问:“死没死人?”如果答案是“没死”,他可能就不会关心这个事情了,毕竟那不是我的财产,我对之表示最低度的同情,但是绝对不会拍手称快。但是,如果我再问一句:“你知道吗?烧的是清华大学的房子啊!就是那栋清华最有名的建筑。现在正在施工,要迎接百年校庆呢!”这时,可能就会出现网络上的那种情况了——非但不会同情,反而会幸灾乐祸。可见,决定一个人对事件抱有什么情感,不取决于“烧了房子”,而取决于“烧了谁的房子”——必须要将财产与财产所有者的社会身份联系起来。而这时,情感的发泄对象也不再是那栋房子,而是房子所象征的社会身份。对房子失火将抱有什么看法,其实完全是对那个房子所归属的社会阶层的看法。那些叫嚣着的网民就好像在说:看看!你们精英的标志现在“灰飞烟灭”了,而在这场大火中,我们大众活跃起来了,我们是真正“浴火重生”的!

在准备这篇文章的时候,我发现这种似乎有些病态的社会心理在其他的事情上表现地更极端。几个月前,上海大众的总经理在一场车祸中遇难,有个人就看到了跟清华学堂火灾之后一样的现象: 上海大众总经理刘坚和公关总监曾家麟等人遭遇车祸不幸罹难,令业界所有人震惊!造车的人,死在车祸中,这就更让人感到心情沉重。然而,可悲的是,网上有人竟然幸灾乐祸,用恶毒的语言咒骂死者,说这是上海大众“饥饿营销”、“加价销售”的“报应”,并借此攻击大众汽车的安全问题。

我不想猜测,这类缺乏起码人性关怀的恶意攻击是来自上海大众的竞争对手,我相信所有对手都会毫不犹豫地赌咒发誓说这绝不是他们干的,毕竟,他们明白,这实在是太缺德了,是在挑战人们的道德底线。但个别隐名埋姓的网民就没有这些顾虑了……对这些内心极为阴暗、对生命毫无敬畏之心的人,我无话可说,但对他们提到的“饥饿营销”和“安全”这两个颇具煽动性的问题我还是想多说几句。

……[2]

让我们再次使用刚才的那套解构逻辑:如果我们跟一个普通人说,“听说又出了车祸了,车里人全死了,可惨了!”这个人可能会用中国最朴素的宿命论感叹一句:“这就是命啊!”但是,当我们补充上这个死者的身份:“死的人是上海大众的总经理!他是造车的诶,竟然出车祸死了!”这样一来,我们如果听到“死得好!”“漂亮!”“活该!”这样毫无同情心的话也就是正常的了。

在跟一个同学聊天的时候,我曾经说过,现在很多新闻报道都很有意思,比如说,有个司机酒后驾车撞死了7个学生,报纸会特意告诉你:“宝马车司机酒后驾车撞死7名学生”。其实平心静气想一想,不管是宝马还是夏利,撞死了7个学生,罪恶都是一样的。但问题就在于,“宝马车”是有权人和有钱人的象征,新闻标题上一加上这三个字那可就是微言大义春秋笔法了。全社会的人都会在这件事上义愤填膺,这时我们的印象好像是,大众是正义的,就像在“李刚门”事件上,大众是正义的一样。但我们可能会忽视的一点是,清华学堂失火、“大众”总经理遇难之后大众的“病态”反应,和宝马车撞人事件和“李刚门”事件之后大众的“正义”反应遵循的都是同一个逻辑:不管是受害还是加害,被关注的群体都是“精英”,反对精英的都是“大众”。而在这种二元对立中丧失掉的,则是人性的共通性,是“同情心”。

心地的温情,是大自然把眼泪给予人类的同时赠与的礼物。

可以肯定的是,怜悯心是一种自然的感情,它能缓和每一个人只知道顾自己的自爱心,从而有助于整个人类的相互保存。它使我们在看见别人受难时毫不犹豫地去帮助他。[3]

以上是卢梭在《论人与人之间不平等的起因和基础》这本经典名著中所说的话,他认为同情心是人性中普遍共通的、最自然的情感。然而,在当代中国,我们却发现,“同情心”也开始分阶层了。我会不会同情你,取决于你是不是属于跟我们同样的阶层。卢梭的《论不平等》正是要研究同情心是如何被人的理性所掩盖的,而我们这篇文章的目的也是要看看现在的社会心理是如何被扭曲的。

二、站在精英的角度看民众:托克维尔—布鲁姆学说的有效性及其局限

让我们回到关于清华学堂失火的那场讨论上。正像清华学生在那篇总结性帖子中说的那样,在他发第一个帖子(也就是本文在讨论网民反应时引用的第一段话)之前,网民们只是表达自己的不同情甚至是幸灾乐祸。清华学生的那个帖子其实是整个讨论的一个分水岭,在那之后,网民的讨论完全脱离的那场大火,而游移到了对一些深层原因的探讨上。比如“清华精神的堕落”、“对资源分配的不满”以及“社会的不公”等等。而最让人看着不舒服的是,对这些问题的“讨论”其实都是一些粗俗的谩骂,我们看到几乎每个反对清华的人都是带着脏字说话的,而清华的学生则从始至终条分缕析地表达自己的观点,纵然很多时候也能看出来情感过激的一面,但他们也一直在避免言语伤人。可以说,他们绝对是“理性”的网民。可是为什么他们的话会遭到这么严酷的讽刺和挖苦呢?我们再次来看那个帖子: “中国人就是这个样,能有什么办法?我们只是觉得委屈,难道我们一天天努力地跟狗一样,为了就是振兴你们这群人的中华吗?……你们纳了点的税款,就对社会做出了比两弹一星大了无数倍的贡献。反正社会上清华人少,非清华人多。清华人所能做的,只有沉默。行胜于言,你们再说也改变不了什么,清华,永远是清华!”

在这段文字中,我们可以很明显地看到,清华学生是站在典型的精英主义的立场上来表达自己的看法的。他首先区分了“我们”和“你们”,“我们”是清华人,而“你们”则是这些没有丝毫同情心,因此没有健全的人格的网民——虽然清华人没敢像我说的这样直白。而我认为最容易激怒大众的一句话就是:“难道我们一天天努力地跟狗一样,为了就是振兴你们这群人的中华吗?”

我很能理解清华学生的感受。他们是“书生”,未来将会是社会的精英,他们具有那种传统儒生的精神境界:“为天地立心,为生民立命,为往圣继绝学,为万世开太平。”他们也主观上认为广大群众会认可他们的理想并且与之一同进步。这也就意味着,在他们的想象中,作为社会精英的“我们”承担起了治国平天下的重任,作为大众的“你们”自然是会买账的。可他们看到的却是完全相反的情形,因此,精英的理想被挫伤了,他们表达出了对平民大众那种非理性心态的极端不满。

我在这里要明确表达自己的观点:在这场争论中,我跟清华学生一样,对网民的那些谩骂和攻击非常反感。这并不是因为我不认同他们的观点,而是我极端反对这种非理性的表达方式。但这并不意味着我赞同清华学生的那种言论,但这一点要留到下文再说。目前很多知识分子都提到了“大众的非理性”这个问题,我在很多的文章中也批判过这一点。正因如此,目前的很多精英,不论是持有传统儒学观点的还是西方古典政治哲学倾向的人,对“平民精神”进行了批判,可以说非常类似于鲁迅先生《文化偏至论》的观点。平等的社会解决了黑格尔《精神现象学》中提出的每个人都有的那种“获得承认的欲望”的问题,但却造成了尼采所说的“末人”(last man),末人的精神就是平民精神,他们并没有启蒙运动所描绘的那种健全的理性,只关心一己之私利,对于伟大崇高的精神生活不具有任何兴趣,甚至在根本上嫉妒和怨恨出类拔萃的精英。这样的社会是不会有任何超越性的,甚至还会在虚无主义的大环境下走向没落。托克维尔早于尼采认识到了平等的社会中将会出现的那种问题,他在《论美国的民主》一书中将自己的观点写了下来,这种观点受到了美国保守派哲学家艾伦·布卢姆的吹捧: 民主政体鼓励人们算计私利,只有很少的人学会了把理性运用于这件事情之外的目标,所以,考虑到他们既无时间也无能力做出判断,他们在许多问题——事实上是所有的问题,因为一切事情都需要鲜活而独立的判断力——上需要帮助。甚至他们所计算的私利——各种目的——也是不确定的。某种形式的权威对大多数人来说经常是必不可少的,对所有人来说至少在某些时候是必不可少的。[4]

但是,这种权威往往不是作为“少数人”的“精英”的声音,而是多数人所形成的“舆论”,是“大多数人的共同信念”,因此,“理性解放的荒谬结果是让人更多地跟着舆论走,是对独立精神的削弱。”[5]所以,精英要在这个平等主义的平民社会中生存,就必须服从舆论,因为一旦他反对了舆论的风向,就成为了“人民公敌”,成为了平等制度的威胁。因此: 在托克维尔看来,在民主体制中,这种类型的人——理论型的人——的生存可能是最受威胁的,要使人性不致丧失殆尽,必须给予强有力的保护。现代民主下的理论思考,大多可以被解释为是平等主义对以帕斯卡为代表的更高境界的人的怨恨,毁坏他的荣誉,扭曲他的形象,抹杀他的存在。……即使他真的出现了,我们也会对他的卓越视而不见,我们会对他引起的不愉快避之唯恐不及。[6]

布鲁姆用美国社会的大量例子来证明这一点。而他的理论立足点在于,要让大学跟社会保持一定的距离,大学的围墙应该是用来保护民主社会中的精英的,让他们免受平民意识非理性的侵扰,这样一来,那些精英们就可以完全独立自由地将自己的理性运用到那些崇高的学问上去。

如果仅仅从现象上看去,我们发现,托克维尔和布鲁姆以美国为背景所描绘的平民精神的泛滥,似乎跟网络上关于清华学堂失火事件的辩论非常相似。这里面节选一段完整的对话来说明这一点。为了让读者看的更加清晰,我们将代表清华大学学生的观点称为“正方”,反对其观点的称为“反方”: 【反方】法国网友:我曾经在清华学习半年,发现清华大学不像学校,很像大市场,什么东西都卖;后来发现,清华其实早就死了,现在只不过别人盗用其名而已!!!!! 【正方】清华大学网友:你只来半年?你没有发现清华精神的权利。 【反方】浙江杭州网友:清华的精神究竟是什么啊?是发现还是发扬?我希望这位学子能教下我们这些愚人,谢谢。 【正方】清华大学网友:你给杭州丢人! 【反方】云南昆明网友:你别管别人丢人不。请解释下清华精神?说啊。狗屁! 【正方】北京网友 [清华科技园]:天行健,君子以自强不息;地势坤,君子以厚德载物,这就是清华精神你懂么? 【反方】浙江网友 [一针] :天行健……耳熟……好象是脑白金的广告吧。 【正方】清华大学网友:这两句话出自《周易》,意思是作为君子,应该像天宇一样运行不息,刚毅坚卓;作为君子,接物度量要像大地一样,用深厚的道德修承载万物。最早是梁启超在清华作了题为《论君子》的演讲,用这两句话激励清华学子。因此清华将这两句话写入了校规,后来逐渐演化为校训。 此外,清华校风是行胜于言,这句话一直深深刻在清华学堂对面的日晷上,而清华学子也确实用实际行动践行着这句话。清华学子也许从来不属于说话最娓娓动听的人群,但是一定属于最踏实肯干、富有实效的人群。 【反方】浙江宁波网友:打的字不少, 但要说起踏实能干富有成效……嘿嘿…… 【正方】北京网友:典型的阿Q!在网络上这样的人越来越多,比阿Q还差的是多了很多骂人的声音。真是悲哀!! 【反方】北京网友:《周易》是个讲鬼神的书,清华还拿来做校训,我笑而不语,哈哈! 【正方】北京网友 [老君洞居士] :没读过周易,竟敢在这里摇头晃脑地胡说八道。这人好可怜。 【反方】广东深圳网友:一本风水书罢了,还不如小学生手册。

如果仅仅看这些言辞,我们很容易就知道,那些反对清华大学学生的人完全是在无理取闹。他们自称是平民,是“愚人”,他们对于那些清华学生对校训的阐释根本不关心,甚至说《周易》是个“讲鬼神的书”,是本“风水书”,“还不如小学生手册”。难怪对这种无理取闹的观点看不下去的人会说他们都是“阿Q”。我们看到,这些现象确实意味着“平等主义对以帕斯卡为代表的更高境界的人的怨恨,毁坏他的荣誉,扭曲他的形象,抹杀他的存在”。布鲁姆用的这些词用在上面那段对话中简直可以天衣无缝。

我们完全可以去批评甚至鄙视中国的大众素质,因为仅仅就事论事来说,他们的言论是愚昧的、无知的,如果是陈独秀在世,我相信他会极尽一切手法对那些人进行挖苦和讽刺——当然,是在他1919年剧烈向左转之前。但是,仅仅从个人品质上批判民众,也许阻碍了我们分析事情的本质。虽然,正像鲁迅在《文化偏至论》一文中说的,中国人的精神若想进步,就必须让每个人都“发扬蹈厉”,发挥其“意力”,也就是意志。但是,如果我们的目的在于对目前的现象做一个深刻的社会科学分析的话,就不能停在对民众个人素质的批判之上了。我看了那些反对清华学生的言论,其中有一句说得其实很引人深思:“网络暴民也只是一群在生活中被压抑了的平民而已。”那么,他们是如何被压抑的?

还是从托克维尔、布鲁姆他们的观点谈起。我们提到过,这些观点跟上引那段对话中表现出来的平民精神现状可谓是完全相符。但是,托克维尔、布鲁姆是以美国这个典型的民主国家作为样本来阐述自己的观点的,而这与我国的国情相去甚远。但是,二者之间之所以有可比性,其原因在于,自1911年共和革命以来,民主共和的观念确实对中国社会产生了很深远的影响。不管社会的实然情况是什么样子的,不管政体形式发生了多么重大的变化,其遵守的政治哲学原理有多么根本性的不同,“民主”这两个字在中国的政治宣传中从来就没有被放弃过。军阀时期谈民主,国民党统治时期讲民主,延安时期也是民主,在新中国之后的毛泽东时代里,民主更是被推倒了极端,要“四大自由”,要“大民主”。改革开放之后,更是要建立“中国特色社会主义民主”。当然,在这些“民主”下面,洛克的古典自由主义、卢梭的人民主权论、马克思的工人阶级专政论和儒家共和主义相互激荡、互相冲突,有些能够导向真正的民主,有些则很容易在实践中走向反面。但这些不同只有经过严格的政治学训练的人才能分析出来,对于中国最广大的民众来讲,“民主”就是那个“民主”,就是国家的事情由人民做主,就是大家都是平等的。传统儒家的那种精英主义意识形态已经破碎了,宪法规定了人民的平等权利,人民也知道自己应该是能够享有这些平等权利的,他们之所以不能捍卫自己的权利,是因为他们没有“权力”——如果我们将“权利”定义为“权力”的正当化的话,我们可以说现在的民众没有“权利”;但如果我们将“权利”仅仅看作是一种“声明”,就像《利维坦》中描绘的自然状态中每个人都“宣称”对任何事物享有权利一样,那我们可以说这种“权利”是存在的,只不过他们没有“权力”,也就是“力量”来保障自己权利的行使。因此,当网络给了他们一个自由的空间,让他们可以对某些不直接触动社会稳定的事件进行评论的时候,他们就获得了这种权利,因而也就具有了民主社会之下的平民精神——托克维尔、布鲁姆所论述的现象就出现了。正像一篇网络评论所说的那样: “互联网时代以技术进步打破了信息传播的屏障,使民众的自我启蒙成为可能。这既动摇了自近代以来中国知识分子所扮演的启蒙者的地位,以及其作为社会启蒙导师的权威。互联网既从技术手段上削平了身份的高度,也通过提供海量信息,打破了精英及教育机构对知识的垄断。网络使公众获得了接受信息以及意见表达的双重主体地位,这就带来了众声喧哗,知识精英的声音无论再响亮,也只是淹没在其中的一种。在一个民众可以进行自我启蒙的时代,精英自然不可能继续高高在上。”

[7]这与布鲁姆的观点完全一致。

但是,我们不能忽视的一个问题是,我们的国情在根本上是与美国不一样的。因为我们只有在网络上才能够有这样的开放空间,而这种空间在真实的社会生活中是很小的——中国的一切问题都可以被归结到“名实不符”之上。并且我们可以看到,网民们讨论的问题其实都是宏观的社会问题,正像清华学生自己说的,都是些关乎资源分配不公平这样的大问题。这其实表明了这一最根本的问题:在中国社会中,“声明”总是很理想的,而“现实”总是很残酷的。我们可以在口头上谈“权利”,但是我们没有“权力”来捍卫“权利”;我们可以大谈精英要为社会服务,精英必须尊重民主法治,但是,现在的精英已经有“寡头化”的倾向了,他们的内心根本没有“制度”的概念。同样的,布鲁姆倡导大学必须要与社会保持相对独立,以保护自由知识分子的理性思考能力不被平民精神侵蚀。他之所以持有这样的观点,是因为他对于美国的大学有这样一种信心——只要按照我的建议去做,大学就一定能够履行自己的使命和承诺,知识分子将保持独立的人格,不对任何政治势力阿谀奉承,并运用理性为人类贡献崇高的思想和价值。但是,中国的大学有这样的信心吗?清华大学有这个能力吗?清华大学的学生一再用解经学的方式强调自己立校的精神,强调清华校训中对理想人格、君子或者说精英的那种规范性要求,强调“行胜于言”的学风。但是民众关心的是,你能不能实现你自己的承诺?如果不让我们这些“愚人”骂你,清华大学难道就真的能够培养出校训中描绘的那种理想的精英了吗?

三、站在大众的角度看精英:大众的无奈与精英的背叛

事实上,这就反映出来了当前中国社会结构中存在的深层次矛盾——大众对精英的根深蒂固的不信任感。清华大学的学生提到了这场讨论的性质:“这场争论已经变质了,成了一种围攻或集体泄愤,远远不是一个交流和吸取建议的平台,已经毫无意义。大部分的人热衷于发表自己的观点,而从未打算认真思考别人的思路,也没有心胸接受有益的意见,唯一想要的就是发泄,因而,单方面的宣泄造成了集体的混乱。”不知道他意识到没意识到这种现象后面的本质——大众之所以“单方面宣泄”,是因为他们根本不认同他们的对立方,也就是精英的思路;纵然精英的思路是正确的,他们也不会相信。他们只在乎“说话的人是精英”,而不在乎“精英说的话”。只要你是精英,那么我大众就一定要反对你——这是派性的对峙,而不是理性的交流。而大众之所以养成了这样的习惯,是因为,他们不再相信这些精英——甚至是知识精英——能够为我们社会的良性发展做出贡献,能够协调这个社会的整体利益,而沦落为了只会为自己利益着想的“伪君子”。上海同济大学的朱大可教授在一个访谈中对这一点做出了一针见血的评价:“至少在1980年代,中国的社会精英仍然在起到一个古典知识分子应当起的作用,就是寻求社会的核心价值,并以此来引领民众的精神取向。这个社会的核心价值就是指民主、自由、个人的独立、尊严、以及教养等等。这一核心价值在1990年代全部被解构了,只剩下两个基础价值,一个是钱,一个是权。这两个最不应该成为核心价值的东西,却成为中国社会的核心价值,所有我刚才讲的这些东西完全消失了。很多精英放弃了对核心价值的探求。”[1]而我们即将看到,网民对清华大学的攻击,就集中在这个问题上。 没有政府力挺,清华算个鸡巴,全中国就清华人跟狗一样的努力吗?你们努力的同时得到了更大的报酬,你们清华人多少是国家培养起来到国外一去不复返的?烧就烧了,外界还不能说两句了,还不内省。

幸灾乐祸是不好,也是国人不应该的!可是你们清华人和以清华为荣的人你们自省过吗?今日之清华是昔日之清华吗?今日之清华有真正的民主、自由的学风吗?你们还有为天下百姓担当的骨气吗?你们的校长在积极参政为了光明的仕途,你们的教授忙着拉项目、到处讲课收钱,你们的学生集天下学子之精英,却仅仅为了自己的前途而奔波,你们的价值就是这吗?你们的建校是庚子赔款美国退款的产物,西方的好思想曾由你们推动,这在历史上是不容抹杀的。可是今日的你们,将这些仅有的价值全都遗忘了,只是为了自己。最关键的是你们在摧残中华文化上做了不遗余力的贡献…… 清华就是给美国培养工程师的。

百年学府今已殚
牛面书生俱做官
沽名钓誉或觉惭
冲天一炬赴楼兰

清华表示:老房子烧了,丝毫不影响我们建设美国的宏图大业。

这个学校有着中国最好的教育资源
不知道现代清华的学生给这个社会带来了什么
又改变了什么?

无非只是你们跳往USA的跳板!

阿Q也罢,骂人也好,至少我们这些非精英还在关注清华,说明清华在普通人心中的地位还是非常高的,至少在我们那个时代清华是一代人的梦想,可我们这些平民的梦想变成了什么样子?那几句口号?那些踩在平民脸上的官员?那个把平民的鲜血当做翅膀的人?那些有好同学可以一起吸血的商人?那些在拿着清华毕业证的外国黄种人?我承认你们是中国的精英,有知识,有能力,但请毕业了的清华学子再看看,你们还敢称自己为君子?

以上是我从论坛中摘录出来的批评清华的话。我们可以看到,大众对清华的批评集中在“出国”这个问题上。他们认为,这一点集中体现了清华人对社会的“背叛”。我其实是最近才切身感受到人才流失这个问题的严重性的。我记得几年前,主流媒体一直在宣传说,现在国家发展得好了,很多出国留学的人都选择回国了,甚至因为回国的太多,“海归”都不值钱了,成为了“海待”。但是,他们并没有说明这些回来的人是因为什么原因回来的,他们也并没有将这些人的学历背景公布出来——这些回来的人,都是常春藤名校毕业的吗?是加州大学校系的学生吗?还是都是所谓“西太平洋大学”这样普普通通的大学的毕业生呢?我们不知道。而上个月听了新东方某著名教师在北大的宣讲会,我才意识到中国人才流失的问题其实一点也不乐观。在那场报告会上,该老师上来就炫耀自己的身价——月薪几位数、上税几位数、房子有几套、车子有几辆……在讲课的过程中,他展示了好几个他认为优秀的学生的照片,每一张都是这个学生跟他的车子还有在美国的大“house”的合影。他为听众描绘的前途是这样的:只要考好了GRE,就能申请好学校;只要从好学校毕业,就能在美国找到好工作;只要找到好工作——他举的都是美国投行的例子——就能有票子、车子、房子,甚至是这个名牌的香水,那个名牌的服饰……他专门举了一个北大女孩的例子,说她每天晚上准备GRE到凌晨3点,而现在她已经成为了美国的中产阶级上层人士,享受法国香奈儿的香水等等。我更加惊讶的是,他说这些话时候没有任何的愧疚感,而完全将其视为理所应当。后来我才知道,这并不是个例,而是新东方老师的普遍观念。如果努力学习仅仅是为了到美国赚更多的钱、享受更多的奢侈品的话,那么学术的真精神何在?知识精英的真精神何在?一身名牌、满脸浮华的社会上流人士又跟一个不学无术的人有什么区别?如果所有的精英都这么想,那么,就像朱大可教授说的,谁来建设社会的核心价值?没有人。精英们都将眼光放在了追求金钱之上了,哪里有更多的钱,哪里就是他们向往的地方——特别是清华北大的精英,他们中的很多人确实是一去不复返了。他们可以完全抛弃“国家”的观念——抑或,我们不谈“国家”和“民族”至上,因为这二者已经略带贬义了,只谈乡土情结,他们难道连这一点也没有了吗?对利益的追求腐蚀掉了一切崇高的理想,“有奶便是娘”成为了这些人的准则。我并不是一个极端的民族主义者,我也尊重个人选择的权利,想在哪个国家生活那确实是我们无法干涉的,世界公民也是我们最终的理想。但是,这一切都建立在一个前提上,那就是我国的发展水平已经步上正轨,并且能够与那些先进的国家平起平坐。只有在发展平衡的基础上建立起来的世界主义才是有效的世界主义。那时候,欣赏西化生活方式的中国人可以去欧美生活,而喜欢东方文化的欧美人也可以到中国生活。而他们之所以能够适应彼此的生活,是因为双方都能够在社会建设上对普世价值表示尊重。但是,在目前的阶段看,网上那些大众对清华学生的批评难道都是无理取闹吗?至少在这一点上是切中要害的。

其实,每个人的心中都有一个对中国教育的总体期望。在任何一种社会中,教育都是塑造精英的最普遍手段。当一个社会的教育体制体现了机会均等这一理念的时候,就意味着,任何人都有成为精英的机会。而一个社会的大众是否对受过高等教育的人表示尊重,要看他是否能够不抛弃对社会整体的关怀。也就是说,民众的期望是,既然受过了高等教育,那么,他就一定有更强的能力和更高的境界,这样一来,他就能够跳出一己之私的狭小范围,去对社会政治的整体利益进行思考。而现在的民众看到的反而是,这些人在大学中学会的知识,只是更加强化了他们为一己之私谋取利益的能力——他们非但不去解决这个社会当中存在的问题,反而因为有了更强大的智力和人脉资源,继续扩大着这个社会中的不公正。当大众仰望着精英的时候,精英几曾回头注视过大众?正像网民对清华大学学生的批评那样,“你们还有为天下百姓担当的骨气吗?你们的校长在积极参政为了光明的仕途,你们的教授忙着拉项目、到处讲课收钱,你们的学生集天下学子之精英,却仅仅为了自己的前途而奔波,你们的价值就是这吗?”“我们这些平民的梦想变成了什么样子?那几句口号?那些踩在平民脸上的官员?那个把平民的鲜血当作翅膀的人?那些有好同学可以一起吸血的商人?那些在拿着清华毕业证的外国黄种人?我承认你们是中国的精英,有知识,有能力,但请毕业了的清华学子再看看,你们还敢称自己为君子?”当接受高等教育已经成为了一个人能够更大限度地攫取社会资源的跳板之后,大众就会非常自然地产生这样的印象——教育已经阶级化了,成为了一个与大众格格不入的东西。甚至,他还在剥夺着大众的基本生存权利。在很多反对清华大学的言论中,人们都将清华学生优越的社会地位和广大老百姓的艰苦生活并列在一起,虽然他们不一定直接指出正是清华人剥夺了他们的生活资源,但是频频将这二者相提并论,不得不让人由并列性的联想产生因果性的联想。

四、知识精英的命运:从超阶级人士到特权阶级成员

文章进行到这里的时候,我要特意论述一下知识精英在当前社会中的位置。在前文中,我并没有区分精英的种类。事实上,如果粗略来看,精英分为三种——政治精英、经济精英和知识精英。当然,这三种人不是截然分裂的,很多人身兼数种精英,比如目前中国很多村干部也是乡镇企业领导,这种人身兼政治精英和经济精英两种身份;而那些对国家政策有影响力的智囊们,则是集知识精英和政治精英的身份于一身。但是我之所以将知识精英单独拿出来说一说,是因为,在理念上,他们与前两者会有着本质的不同。政治精英和经济精英都有社会关怀的一面。但是,由于支配政治行为的是权力逻辑,支配经济行为的是经济理性,所以,他们不可避免要将大部分精力耗费在为自己的前途进行筹划之上——政治精英要打算着如何保持权力并扩大权力,经济精英则要运用经济理性来推进个人利益最大化。他们都有强烈的利益概念。而只有知识精英是最有可能从超越利益的角度来考虑问题的。因为知识精英在气质上是理想的守护者,也是真理的探寻者,从理念上讲,他们其实并不对社会任何一个集团负责,而只对真理负责。按照希腊古典哲学的传统,“理想”就是“理念”(eidos),“理念”就是“真理”(aletheia),而真理一定是通过“理性”获得的。因此,理想与真理是一致的,知识分子的最高理想就是找到那个最高真理,并用这个真理改造社会,或者哪怕无法完全改造社会,也可以将其作为社会核心价值观,让所有人对这一价值表示尊重。启蒙以来,知识分子受到了更大的重视,由于对理性的崇奉在这个时代里达到了前所未有的境地,“设计社会”的理念应运而生了。知识分子相信通过勇敢地使用自己的理性,可以设计出一个合理的社会。事实上,他们没有完全高估自己,美国就是一个被设计出来的国家,其所有权力制衡的机制都是符合“政治科学”的逻辑的。知识分子从来没有被赋予过这么大的权威——他们不光要设计社会,还要让所有人都具备使用理性的能力,这才是真正的“启蒙”。知识分子是高高在上的,但这并不是因为他们具有政治权力和财富,而是因为他们声称能够为整个社会提供真理,因此他们理所应当地站在理性的高度上,一方面来批判传统宗教和迷信的荒谬以及贵族和君主的腐败,另一方面来批判大众文化的愚昧落后,并用理性之光教化民众。知识精英是超阶级的,因为真理是不分阶级的,只有“意见”才会因人而异。

回到当代中国,我们发现,这种经典的现代知识分子形象在中国表现得并不明显。原因是知识分子很难具备独立人格,他们的研究受政治的干扰程度可能会很大。我不想再列举那些大嘴巴经济学家说出来的激怒群众的言论了。我知道现在中国还是有独立的知识分子的,但让我感到不安的是,这些知识分子的理想不会被大众所理解。我曾经指出过社会存在和社会意识的分离:就比如,“官二代”、“富二代”肯定不都是李启明那样的人,因为按照亚里士多德的理论来讲,财富是一个人修炼德性的“外在善”,有财富并不意味着就必然会堕落。但是,因为媒体反复曝光李启明这样的人,普通百姓会自然而然地产生这样的意识,那就是,“官二代”和“富二代”都是李启明,他们的爸爸都是李刚!这就是标签化、符号化观念的产生。目前社会对精英的看法也是这样:“你们的校长在积极参政为了光明的仕途,你们的教授忙着拉项目、到处讲课收钱,你们的学生集天下学子之精英,却仅仅为了自己的前途而奔波,你们的价值就是这吗?”这样的话语还算是公允的,但是一联想到现在的社会一提到“专家”就说他们是“砖家”,一提到“教授”就说成是“叫兽”,知识精英在大众眼中的整体形象就可见一斑了。很多人将没有成功预测地震的责任也推到了专家头上,而很多教授不检点的个人作风又让民众抓到了把柄,在加上前面提到的教授行政化、商业化……这样一来,当知识精英也被当成是跟政治精英和经济精英一种类型的“特权阶层”之时,他们想在社会中发挥启蒙作用的理想就不会成真了,那些真正独立的知识分子也根本无法施展自己的抱负了——因为知识精英的整体形象已经被玷污了,他们已经失去民众最基本的信任了。你说出任何的话,哪怕是理性的,都会被民众“不惮以最坏的恶意”来曲解掉。

这种危险在中国的第一次启蒙时期就存在过。

新文化运动是中国近代最为人称道的一次启蒙运动,陈独秀、胡适、鲁迅等人希望让中国的民众都具有科学的精神,这种科学的精神其实就是启蒙思想家所强调的“理性”。这是一种很崇高的理想,他们最终的目的是根本改变中国人的国民性,改变中国人的愚昧和落后心理。为了能够达到普遍启蒙的目的,并且让广大民众感到平等,他们力主用白话文取代文言文。他们成功了,五四一代认为这将为启蒙铺平道路,因为白话文将拉近精英和大众之间的距离。但是反对的声音一直都存在,这其中有“非激进主义者”章士钊,他以那些“新青年”们不具有的冷静口吻告诉后者,不要光关心白话文还是文言文的问题,问题的关键出在新兴教育方式之上。 “新式教育是独占的教育,与乡村人的职业竞争和社会地位都不相干,因此只能吸引或接受远较老式教育制度为少的学生。‘今之学校,自成为一种贵族教育,故其与文言白话之争,了不相关。’”[2]

这种批评并不是无效的,知识精英确实是有传统儒家那种兼济天下的情怀,但是,由于那时中国现代化发展非常不平衡,因此,只有那些真正有财力,并且有现代见识的人才受得起现代高等教育,而他们跟广大群众有着一种脱节的趋势。 “北京的学院派知识分子,通商口岸的‘时髦’知识分子和专家,甚至还有许多特殊时期的激进学生,这些人都把过去抛在了身后。但在内地的广袤乡村,在各省无数的嘈杂里弄,甚至在大都市拥挤不堪的背街小巷,则是另一种情况。”[3]

柯文在对义和团运动的研究中发现,新文化运动时期的知识分子不单单忽视了这些平民大众的状况,更是对大众文化有着深深的鄙视: “1925年历史学家顾颉刚写到:‘我们一班读书人和民众离得太远了,自以为雅人而鄙薄他们为俗物,自居于贵族而呼斥他们为贱民。’顾所说的这种精英意识,12年前蒋梦麟就做过公开表述,他有意识地写道:‘我们的座右铭是:由知识阶级建立为人民的人民政府。’顾和蒋的言论清楚地表明,鼓吹新文化的知识分子对中国传统文化所持的否定立场也有其阴暗的一面。它并不仅仅是现代主义者对传统的攻击和新文化与旧文化的对立,它还反映了精英阶层厌恶亿万中国老百姓参与其间的大众文化的倾向。……中国的知识精英和政治精英内心里是鄙视大众文化的。”[4]

其实,蒋梦麟后来也认识到了新文化运动当中的隐忧。1920年他在一篇文章中说: 若新文化运动的唯一成果是提高了知识阶级的权威,那全国民众却没有从中得到什么好处。

社会进步不是少数知识分子独立完成的事。只有大多数农民获得进步才能完成。若社会中少数人天天讨论文化,大多数人还不知地球是圆的,社会因此而分裂成两个互不相关的世界,它怎么会进步?

……你们知识分子,是我们社会的精英,将来农民开始进步时,你们会失去自己的地位。[5]

由此我们就看到这种危险所在了:那些声称要“设计”一个新的中国的知识分子们,在不经意间已经具有的阶级的身份。如果我们退一步说,知识精英鄙视大众文化也并不是问题的核心,因为如果知识精英不先鄙视大众文化的话,何谈要根本改变中国的国民性呢?对大众文化中平庸、愚昧、落后的部分进行反思和批判,并为大众提供一个启蒙的目标,这并不是一件“阴暗”的事情。但是,如果受过教育的知识精英在社会认同上完全与普通大众划清界限,口头信誓旦旦要“为生民立命”,而并没有以真实行动建立与大众的联系的话,上述的问题就出现了。知识分子被大众看成是一群已经“阶级化”了的人,他们无法完成他们声称要完成的设计社会、启蒙大众的任务了。因为大众大可以揣测说,他们会不会是为了自己的利益而不是为了整体利益?那些西装笔挺、生活优越的知识分子,与官僚、军阀这些社会毒瘤在外表上有什么不同吗?

历史证明,最后彻底批判新文化知识分子的不是章士钊那些保守主义者,而是左派激进势力,这当中,陈独秀自动反省了,他开始站在胡适的对立面,而后者依然坚持新文化运动时期的启蒙理念。毛泽东等新知识分子加入了进来,而正是他在整个中国历史上对知识分子传统进行了最彻底的颠覆。在《延安文艺座谈会上的讲话》中毛泽东说,在学校里,他觉得知识分子是最干净的人,工人农民是比较脏的;但是革命之后,他转变了认识:“就觉得知识分子不干净了,最干净的还是工人农民。”[6]毛泽东认为知识分子是资产阶级和小资产阶级的,而正是从这里开始,革命意识形态在中国正式兴起了,对于知识精英总体上属于一个特权阶级的概念,原先仅仅是大众头脑中一种松散的观念,是社会中一种危险的倾向,而现它在已经被一种严密的意识形态以“科学”的方式所最终确认。格里德尔将新文化知识分子和革命意识形态之间的区别表述得非常准确:

新文化知识分子坚持精英价值的社会意义;革命者则对知识精英主义表示怀疑,而且把大众的价值作为出发点,或认为精英价值必须包括在整个社会价值体系中。新文化知识分子并非对社会问题漠不关心。但总的来说,他们对以根本性的阶级斗争和矛盾观念为基础的社会变革不表同情,而宁愿强调他们断定具有普遍性的人格品质。另一方面,社会革命家抓住并利用社会和经济不平等的存在,来为大众辩护,为从意识形态上建立社会组织(至少是大众政党)辩护。新文化自由主义者确定的知识分子角色是有责任的社会和文化变革的战略家;在革命制度下知识分子被当作可以信任的伟大的社会和文化转换中的必要的合作者。但他们被剥夺了设计的权威。他们变成了和其他人一样的劳动者,他们是能为建设新制度大厦提供服务的熟练手艺人,而不再自以为是设计师。[7]

革命的意识形态是大众的意识形态,为了动员最广大人民起来进行群众性革命,宣传家们往往会美化大众的形象,将其看成是集中了全部的道德,并且天生就具有民主的能力,只要推倒压在他们头上的阶级剥削和帝国主义压迫,人民就能够获得解放,解放之后的中国将会是一个由人民建立起来的大众政府,是完全民主的,是中国最广大普通人的“千年王国”。但是,我们说了,要动员群众进行革命,就必须预先美化大众,但是这种美化与事实有多大的差距?这其实是一个很值得研究的话题。柯文在他那本杰出的《历史三调》中,已经以义和团运动为个案,对“历史事实”与“历史阐释”的区别这个问题进行了很好的研究。在1919年之前,新文化知识分子一直将义和团看成是愚昧、迷信、野蛮、未开化的代名词,然而,当革命意识形态兴起并且国民革命马上就要开动的时候,为了政治需要,一场美化义和团的舆论战轰轰烈烈地展开了,义和团被描绘成了反对帝国主义的爱国主义者,他们的一切野蛮行为都因为对准了帝国主义的压迫而获得了正当性。这种转变之大,可以从陈独秀相差6年的两篇文章中看出来。1918年,当《克林德碑》在《新青年》发表的时候,义和团被陈独秀以其特有的讽刺口吻批驳得一无是处;然而,等到1924年《我们关于义和团的两个错误观念》在《向导周报》发表后,义和团就成为了民族尊严的代名词。然而历史总归是历史,在现在细心的研究者看来,这种对历史的新阐释只能证明其政治需要——那就是对人民进行美化。而人民是否就真的是那么完美呢?从义和团的很多史料上来看,根本不是这样。

这就是民粹主义的兴起。革命领袖告诉大众,你们要将一切被划为反动阶级的人物消灭。正如李锐的研究所揭示的那样,毛泽东在《中国社会各阶层的分析》一文的初稿中,将中国社会中大部分知识分子都划归到了阶级敌人的范畴。[8]然而其后果也是显而易见的——“文化大革命”就是自从1924年开始的民粹运动的逻辑极端。那是一场彻底的反智运动。毛泽东认为,民众不需要由精英带领着来启蒙,因为他们将在革命实践中自行启蒙。精英灰飞烟灭了,而大众将在革命的烈火中浴火重生。

我花了这么大的篇幅缕叙历史,并不是表明说历史正在重演。中国社会已经经历过现代化的大规模洗礼了,我们现在的社会分化程度应该没有将近100年前那么严重——我们现在的社会分化是在现代社会基本盘之上由分配不正义导致的分化,而不是上世纪初那种前现代与现代的根本分化——但是,这并不意味着那段历史和我们现在就不具有相似性。我们上面提到了,知识精英有成为一个特权阶层的倾向,并且在民众的意识中,它可能早已阶级化了,难道不是吗?民众现在对知识精英根本就不信任,对那些有着“设计师”抱负的精英们不感兴趣,难道不是吗?当一些“书生”希望在民众面前守护君子的理想时,广大的民众却对此嗤之以鼻,认为这是不成熟的表现,难道不是吗?这就是我要批评那位清华学生的原因。那位清华学生上来就说,“难道我们一天天努力地跟狗一样,为了就是振兴你们这群人的中华吗?”联想到清华学生当时的心境,这种精英主义的话语是可以理解的。但是,这是不是又重复了新文化时期知识精英与大众相隔离的历史误区了呢?这是否强化了大众对社会精英的“非我族类,其心必异”的感觉了呢?从后面的争吵来看,确实是这样的。

联想到本节开头时我们提到的三种精英的性质区别,我们也可以推论出社会阶级化的先后次序:当社会分化趋于严重的时候,政治精英和经济精英往往是最先被看作是阶级化了的,因为很明显的是,他们是直接操控权力和财富这两种可见资本的人;知识分子往往是最后被视为是特权阶级的,因为当政治精英和经济精英已经失去民众的信任之后,知识精英往往是最后一个能够坚守本心的群体——自古以来,知识精英就是道德和真理的化身。而一旦知识精英也被视为社会的特权阶级而不再是超阶级人士的时候,社会的极化就很值得堪忧了——知识精英的独立性是社会矛盾激化的最后一道防线。但现在看来,这道防线也岌岌可危了。所以朱大可说,“不夸张地说,现在已经形成一个精英和大众高度的对立。在中国历史上,这种对立通常是社会大动乱的前兆。”[9]而孙立平教授的评论更加深刻:“上层寡头化、下层民粹化是我们必须同时警惕的两种现象。”[10]另一篇文章也证实了这一点:“在网络的语用学当中,‘精英’与‘愤青’已经成了两个势不两立的概念。在缺乏公正的游戏规则下,这一趋势的严重后果,很可能是落入民粹主义和精英主义纷争的泥沼,陷于大众和精英双重背叛的危机。”[11]

五、社会极化的逻辑:从无视精英到反抗精英

从1911年的辛亥革命,到1976年“文化大革命”结束,几代中国人都是在革命中成长起来的。然而,“文化大革命”的破产让全社会都产生了重大的反思——革命其实并不永远都是可欲的。现在是一个后革命的时代,我们的美好希望是,再次确立起来新文化知识分子的理想:通过渐进的社会改革,和平过渡到一个理想的社会中。但是,我们却发现改革已经进入了一个瓶颈期,社会不公正正在凝固化。而上层寡头化、下层民粹化导致的将会是严重的社会对立,也就是“极化政治”。事实上,在这样一种双向的过程中,那种我们现在避而不谈的“阶级意识”可能正在形成。从清华学堂火烧事件的网民评论来看,虽然大多数人表达的是对精英的不信任,而并没有更为极端的举动,但是,一切引人警惕的言论已经出现了。

第一步是不承认清华学生的精英地位,认为大众正憋着一口气要证明自己的力量: 现在的国人不是说你是清华的就让你三分!你要明白,人与人之间的竞争不是高三那一年可以改变的。不是你是中国高校一员就能厉害的,当你在复贴的时候你已经输了,你要是真想证明你们有拯救世界的决定,请你用事说话,记住这国家有比你们强者在忍耐着,因为这样的竞争已经开始了。你们的思维、品行在国人眼里已经出现了问号。

当“你们的思维、品行在国人眼里已经出现了问号”之后,就要考虑抛弃“你们”了:

觉得或许清华的兄弟们智商太高,无法听懂我们这些大众之语。大家不是针对你们,而是针对整个社会,整个教育体系。懂么?我只是想问一句,现在清华学生的理想和志向是什么?无非八成以上是去USA。这不能怪你们,这是社会的错误。我们失去了幸福感,失去了安全感。我们都很恐惧,就如农民工版本的《春天里》为何如此火爆。我们都在恐惧和厌恶这个社会。“如果有一天我老无所依,请把我留在,在那时光里,如果有一天我悄然离去,请把我埋在这春天里”感触了大众。我们需要的是个大众的政府和大众的思想,我们不需要精英!!歌词对改革早期的怀念。大学生在九零年代还有么?还有那种心怀天下的抱负么?还有担负祖国崛起的雄心么?无非也是一帮铜臭熏陶的棋子而已。国家始终是由这些不起眼的个体组成。当你们这些精英和他们背道而驰的时候,国家的命运也是坎坷的。

“我们不需要精英!!”但是,这段文字最后只是说“当你们这些精英和他们背道而驰的时候,国家的命运也是坎坷的”,而没有说“当抛弃你们这些精英之后,国家的命运就不坎坷了”。逻辑的进一步推进将在下面的那段话中被表达出来: 难道你天天努力地像狗一样,农民、工人,那些在四十多度的太阳下搞建筑的就是过的神仙一样的日子,我们纳了点税?是不是纳的少了,不够你们花的,你们为了振兴中华?难道没有你们中国就不是中国了,少自大狂了,难道你们是现在的精英?现代这个社会,也没有看到你们敢于抗争,敢于为民请命,没你们中国还是中国,没有你们中国还是一样前进,但是没有了农民、工人,中国就不是中国了。

“中国”已经不能以精英的价值取向来定义了,因为精英已经不成其为精英了,相反,工人、农民才是中国的中坚,这种思想与革命意识形态不谋而合,因为“人民”的主体成分正是工人和农民。而如果这段话的逻辑再往前推,就是这样一段话: 中国民众必须自觉地用马列主义毛泽东思想重新武装自己。重新学会阶级斗争,重新学会艰苦奋斗,重新学会独立自主,重新学会自力更生,重新学会自辟蹊径自走新路。[12]

我必须澄清,这句话并不是在论坛中出现的,跟清华学堂事件也没有关系,而是出自一个左派分子在9月美军军演之前写下的一篇文章。在我们看来,这些“老左派”的言论是荒谬的,有些不可理解,但是当我们把上面四段话串联起来后,就不得不担忧会不会出现这样的逻辑——从对精英的不信任到对精英的抛弃。上层正在寡头化,下层也在民粹化,如果这个局势发展下去,老左派们的意识形态就将会是点燃社会动荡所需要的最后那一丁点火星。当意识形态深入人心之后,任何理性的对话都是建立不起来的。因为意识形态的激情就像是康德所谓的先验直观和先验范畴,那是一种深植于人的意识最深处的“有色眼镜”,甚至是人认识世界的最根本依凭。如果在这种情况下希望用理性来批判和解构意识形态,就相当于剥夺了对方的立身之本。理性和意识形态是完全无法对话的,而最后的结果将证明意识形态力量的汹涌澎湃和理性思想的孤立无助。

六、我们的期待:理想状态与知识精英的使命

那么,在一个健全的社会中,精英和大众的关系是什么?

任何一个社会都不能没有精英,甚至可以说,在任何的社会中,精英都起着非常重要的的作用。我为什么这么说?难道民主社会不是由人民来行使权力吗?这是从宏观的角度来看的,如果从政治运作角度来讲,我们就会发现,精英在社会中往往起着不可替代的作用。因为,国家这么大,不可能事事都由人民做主。这是因为,现代社会总体上是技术官僚统治的社会,而这样的社会也是一个高度庞杂、非常精密的社会,没有受过某一方面的专业教育,一个人基本上无法对很多专业的政策发表建设性的意见。人人都知道物价上涨是不好的,但是没学过经济学的人在这里面也就只能发牢骚了,因为他们不知道应该采用什么样的经济手法调控这个复杂得让人觉得有些神秘的社会。我们总是关注民主国家全民投票的那个环节,却并没有在意从政策提出到得到人民同意的整个政治过程。事实上,每一项决策都是先经过精英团体的商讨,拿出一个或多个方案,通过媒体向民众进行解释和说明,最后再由民众进行表决的。这些精英是名副其实的领导,没有他们,这个社会将没有凝聚力,其运行将没有任何效率。甚至在危机情况下,民主程序是完全起不到任何作用的,处理危机只能靠治国的精英集团的个人智慧。《惊爆十三天》这部电影里面就表现了肯尼迪处理古巴导弹危机的英明举动——民众并不永远是正确的,当不合时宜地将国家的危险告诉人民的时候,人民可能会帮倒忙,虽然这是个体现透明度的政治举动。而这一切政治评估要靠领导人个人的能力。但这些国家之所以能够处理好精英和大众的关系,关键在于,他们建立好了一套非常精密的权力制衡体制。精英有资格领导这个国家,但前提是他们要得到人民的授权,对人民负责,受人民监督,而不能为了自己的利益而聚敛权力。大众服从精英的管理,但是,一旦他做出了不负责任的行为时,就要受到弹劾,甚至下台——尼克松就是很好的例子。因此,我并不喜欢谈论“民主”这个词,因为我觉得政治设计中最精妙的地方在于权力制衡的机制

中国社会不管向着什么样的方向发展,权力制衡机制是不能不建立的。精英如果真要证明自己的接触能力,就不应该怕被监督,因为勇于接受监督的精英才是真正意义上的精英。在现代社会中,精英没有自然的权力,他的任何权力和权利都是被授予的,都是要经过论证的。但是,这里面还有一个特殊的群体,那就是知识精英,在理念上,他们是直接对真理负责的,而不是对政治社会中的任何群体负责。这个群体没有刚性的生杀予夺的权力,但是他们在塑造社会价值的过程中将起到非常重大的作用,然而它将不受到任何人的制约:正是因为它直面真理的性质,事实上,只有良心和道德才能够制约他们——这是一种软性制约,但也是最崇高的一种制约,因为它是真正“自律”而不是“他律”的。也正因如此,知识精英的堕落将会是最严重的堕落,因为知识精英唯一可以依凭的是道德良心,而一旦这一点破灭,他就将无可救药。在我看来,目前中国社会中精英与大众的对立,主要是精英的问题,因为精英现在已经占有了很多他们不该占有的东西。前面文章第五部分列举的那几段话,都提到了,精英“还有那种心怀天下的抱负么?”“现代这个社会,也没有看到你们敢于抗争,敢于为民请命。”这些都是彻底的儒学式的言论,可以说,很多人还没有意识到“民主”和“民本”的区别,他们认为社会精英是要“为民做主”的。但不管这是不是符合现代政治精神,精英——特别是知识精英——在社会中确实应该起到这样的作用。事实上,知识精英不是民众的代言人,而应该是正义的代言人,当“强者的正义”明显对弱者不利,并且这种不利已经严重威胁到社会健康发展的时候,知识精英确实是要为民众的利益说话的。然而,我们确实看到,知识分子正在遭遇“集体失语”,民众其实很希望看到知识精英对“李刚门”事件的表示,但是精英的举动却并没有让他们感到满意,而我们不得不说,这里面确实有大的政治阻力。因此,网民在清华学堂火烧事件之后的“借题发挥”,其实是对先前积累的合理的社会不满的一种不合时宜的宣泄。他们认错了宣泄的对象,但这种“错认”本身就很能说明问题(有意思的是,他们甚至希望清华学生将在网上反驳网友的力气“用到广场上去”)。因此,要遏制社会分化的趋势,确实要依靠知识精英的道德意志和实践理性。因为上文已经说过,知识精英往往是最后被阶级化的,因此,如果这个防线不破,我们这个社会就还是有希望的。这是,这样的说辞也只是说着容易做着难,因为体制所限,知识精英可能也很难独善其身。一个深刻的社会科学分析是应该看到体制的结构性矛盾的,但是如果从实践的角度来讲,我们又怎么能期待着体制自然而然地向着好的方向演变呢?那只是“进步的幻象”罢了。我们期待着,因为人们的主观努力,社会断裂的趋势可以回转——精英与大众重新建立互信,相互之间能够以理性为准则相互交流,倾听对方的逻辑,认同对方的合理之处。而最重要的是,那种联系人与人之间最深层感情的“同情心”,那大自然赠与我们的“心地的温情”,能够重新活跃起来,去舒缓被当代中国社会扭曲的人心。在这个理想的指导下,知识精英要贡献最崇高的价值和思想,政治精英和经济精英则必须成为行动的中坚。因此,最后以列奥·施特劳斯的一句话为结尾,以作为我们对当今中国全体精英的期待吧:

理性的准则对多数的人们不具有影响,亚里士多德明确地指出了这一点。但是,按照他的看法,对多数人是如此,绝不意味着对人格高尚、胸襟高尚、热爱荣誉的人也是如此。这样的人,崇奉先验概念。[13]

BYVoid註:轉載時註解缺失

Linux内核与glibc

轉自[Fedora 中文郵件列表] 作者microcai@fedoraproject.org

往往内核添加了一个功能, glibc 要花很久才会用上。本来linux 那边为这个功能是否进入内核已经吵半天了,glibc这边又要为是否使用这个内核新特性再次吵架半天 (glibc 不是 Linux 专有的,还得考虑 BSD (虽然人家也不用 glibc),SysV Windows(诶,这没办法),还有 sun 那消亡的 solaris , 还有, 自家的 Hurd。然后,总之,这样新特性让人的接受上。。。 太慢了。

说近点的,fnotify glibc还没有对应的包装函数呢,futex 和 NPTL 又是花了许久才进入主流的。libc 是 app和内核的桥梁,libc 理应快速跟上内核的接口变化。但是 glibc 和内核不是一块开发的,所以,这只是理想罢了。glibc 还要去兼容不同版本的内核呢!

而内核也要去兼容不同版本的 glibc . 双方都背负了太多的历史包袱。glibc 至今保留 LinuxThreads 兼容2.4版本的古老内核。Linux对已经没用,甚至有bug(接口的问题导致一些bug是必须的)的系统调用也必须保留,谁知道用户会用哪个版本的glibc呢?虽然新的glibc会使用新的调用,但是提供和老的调用一致的 API 来兼容,但是,用户只升级内核而不升级 glibc 是常有的事情。就算升级了glibc ,你新版本的 glibc 一定就用上内核的新接口?还是再等几年等 glibc 的开发者吵架结束吧。

于是乎,Linux 的大牛们再次使出绝招: 让 libc 变成 VDSO 进驻内核。 这里普及一下 VDSO 这个小知识,知道的人跳过,不知道的人读一下 VDSO 就是 Virtual Dynamic Shared Object ,就是内核提供的虚拟的 .so , 这个 .so 文件不在磁盘上,而是在内核里头。 内核把包含某 .so 的内存页在程序启动的时候映射入其内存空间,对应的程序就可以当普通的 .so 来使用里头的函数。比如 syscall() 这个函数就是在 linux-vdso.so.1 里头的,但是磁盘上并没有对应的文件. 可以通过 ldd /bin/bash 看看

这样,随内核发行的 libc 就唯一的和一个特定版本的内核绑定到一起了。注意,VDSO只是随内核发行,没有在内核空间运行,这个不会导致内核膨胀。这样内核和libc都不需要为兼容多个不同版本的对方而写太多的代码,引入太多的 bug 了。

当然, libc 不单单有到内核的接口,还有很多常用的函数,这些函数不需要特别的为不同版本的内核小心编写,所以,我估计Linux上会出现两个 libc , 一个 libc 在内核,只是系统调用的包裹,另一个libc 还是普通的 libc ,只是这个 libc 再也不需要花精力去配合如此繁多的 kernel 了。

姑且一个叫kernellibc, 一个叫 glibc : printf() 这些的还在 glibc。open() , read() , write(), socket() 这些却不再是 glibc 的了,他们在kernellibc。

關於騰訊和360的三首詩

姑且把它認爲是詩吧。

驅鬼

山五嶽何爲峰 合之內誰英雄 點可容太極傲 聚億萬布衣龍 得乾坤收日月 掃魑魅魍魎童 架九天皆來拜 至地府十八層

極天諱言

龍孤去九重天 語氤氳宇寰綿 友不成乾坤過 書可破混元箋 魂殤魄鬼爲聻 世飄虛時本淺 界神祗封塵後 元可信我佛言

布鳥里

身即騰處世間 王子蕭月滿臺 誰禪邊征戰苦 三十梯入漢嘆 金鋪落獸鐶空 鬱郁成曉又昏 王敦古月明中 晚景檐溪鳥回