C++语法分析中最让人头疼的歧义

C++是个特别复杂的语言,其复杂性不仅体现在开发模式上,也体现在语法分析上。许多人都遇到过嵌套模板参数的歧义问题,如vector<vector<int>> v,在有些编译器上会被解析为vector < vector < int >> v,但新的编译器都已经解决了。而最让人头疼的歧义则是Most vexing parse

class Timer {
 public:
  Timer() {}
};

class TimeKeeper {
 public:
  TimeKeeper(const Timer& t) {}
  int get_time() {return 0;}
};

int main() {
  TimeKeeper time_keeper(Timer());
  return time_keeper.get_time();
}

以上代码中出现歧义的是TimeKeeper time_keeper(Timer());,因为它有两种理解方式:

  1. 定义一个TimeKeeper类型的对象,并用Timer()作为初始化参数。
  2. 声明一个名叫time_keeper的函数,它的返回值类型是TimeKeeper,参数是一个函数指针,这个函数指针指向的函数的返回值是Timer,无参数。

很明显我们想要表达的是第一种意思,但很不幸编译器会默认理解为第二种。Clang++会给出以下错误:

timekeeper.cc:15:21: error: member reference base type 'TimeKeeper (Timer (*)())' is not a
      structure or union
  return time_keeper.get_time();
          ~~~~~~~~~~~^~~~~~~~~

之所以产生这种歧义,是因为这几个原因:

  1. C++的函数在使用前需要声明,定义和声明是可以分离的。
  2. C++的函数声明的参数可以只有类型,没有名称,如int max(int, int);
  3. C++的函数声明的参数名在类型名后可以加(),如int max(int (a), int())
  4. C++的函数声明可以在函数体中。

最优美的解决方案是使用C++11的统一初始化语法:

TimeKeeper time_keeper{Timer()};

你不知道的东西正在伤害你——浅谈信息不对称

信息不对称(Information Asymmetry)指的是市场上交易双方所掌握的信息不相等,有时候卖方掌握得多,有时候买方掌握得多,因此可能会导致逆向选择。同时,信息不对称的存在产生了信息市场,帮助交易双方相互了解的服务可以从此获利,但如果产生了信息垄断,则会导致寻租行为。你不知道自己不知道什么(You do not know what you do not know),而且你不知道的东西可能正在伤害你。

市场中的信息不对称

多数情况下是卖方掌握更多的信息,譬如在二手汽车交易市场上,卖方很了解出售的汽车,但买方很难有效判断哪个汽车是好的,哪个汽车是差的,因此卖方就存在着隐瞒二手汽车故障的动机。在这种前提下,对于卖方来说总是希望出售差的二手车,以获取更高的收益,因而导致市场上充斥着各种差的二手车,好的二手车退出市场。于是买方购买二手车的意愿就会降低,导致交易成本提高,双方都受到损失。

当买方掌握了更多信息之后同样会有问题,譬如医疗保险市场,卖方对投保者的真实身体状况很难有效掌握,而买方对自己疾病的风险却相对清楚。如果保险公司设定一个费率,那么风险低于这个费率的投保者会不愿意投保,投保者全部都是疾病高风险的人。这样就会导致保险公司亏损,破产倒闭,交易无法进行。为了逃出这个困境,保险公司总是花费巨额成本对投保人的真实情况进行调查,带来了很大的社会成本。

信息不对称的存在还催生了广告产业,由于买家对卖家销售的商品不了解、不信任,卖家不得不花钱做广告。广告最初是帮助买卖双方互相了解,但最终到达不做广告就完全销售不出去的地步。广告产业一方面消除了信息不对称,另一方面还造成了信息垄断,使得卖方可以利用信息不对称给买方带来虚假信息,从中攫取经济租值

恋爱中的信息不对称

年轻男女相互求偶是一个很特殊的市场。请允许我将它抽象为市场,恋爱成功视为交易达成,虽然不涉及金钱交易,却消耗了机会成本,而交易完成以后又给双方带来收益。

在恋爱市场中,男性一般是主动者,女性一般是被动者。优秀的女性会有大量追求者,而其他女性追求者寥寥。由于信息不对称,优秀的女性尽管选择较多,但很难从追求者中鉴别出最优秀的(最适合的)。而男性为了使自己胜出,会使用各种方法取悦甚至欺骗女性(相当于虚假广告的作用),结果总是造成「烂蛤蟆吃天鹅肉」或者「鲜花插在牛粪上」的结果。

信息不对称还导致了「剩女」的问题。剩女有两种,一种是不够优秀,无人追求(第一类剩女),一种是有追求者,但自己总是不满意(第二类剩女)。如果信息对所有人对称,那么前者当且仅当女性人数多于男性时才会产生第一类剩女。第二类剩女只有在不理性时才不会产生的,因为所有人都知道自己能匹配到的最好的交易,预期超过最优值是不理性的。(当然,不理性的女人总是存在的。)

对于男性来说,信息不对称时其取悦表现(广告)带来收益有可能超过了自身(价值)能够达到的最好交易,这样的交易是损人利己的交易,带来的是负面的社会代价。因为这样的恋爱不是对等的,潜藏了破裂的危机。相当于购买者听信虚假广告购买了一个次品,会产生怨气,不利于社会效率。信息不对称给优秀但内向的男性带来的个体损失是最大的,相当于优秀的产品因为没有在广告上投资而错失市场。

在完全信息对称的恋爱市场中,求偶就简化为了稳定匹配问题,所有主动者都能找到自己能找到的最好的选择,资源配置达到了帕累托最优(Pareto Optimality)。

信任可以消除信息不对称

博弈论中经典的囚徒困境问题的根源也是信息不对称导致的,在信息不对称的前提下,人们倾向于自私自利,而不是与人合作,因为与不诚信的人合作是要付出单方面的代价的。因此解决信息不对称最有效的方法就是相互信任。信任可以显著降低交易成本,给全社会带来收益,一个发达的社会必然是相互信任程度较高的社会。

信息市场

主动给受信任的一方披露信息(信号)会给交易双方带来收益。譬如求职招聘市场的信息不对称给猎头公司带来了获利的机会,但LinkedIn这样的网站正在蚕食猎头行业的市场。LinkedIn建立了基于职业技能信息的社交网络,每个人都可以把自己的简历公开,使得招聘的成本大大降低。无论是对于求职者还是招聘者,都从信息透明度增加中获得好处。

同样,Facebook的价值体现在它给全世界数亿人提供了选择性披露自己个人信息的机会,让每个人都能迅速了解到自己社交圈内的每个人。Facebook最早之所以能在美国校园中爆发,是因为它解决了社交圈内的信息需求,尤其是年轻人之间的相互吸引。在社交网络诞生之前,美国年轻人一般都会去酒吧、俱乐部寻求艳遇,但在这种场合下遇到合适的人的效率是很低的,获得超额利益的人往往是长期混迹于酒吧的人,其个人价值并不一定高,而高价值的人却不被人知道。Facebook有效地解决了这一问题。最近一个基于Facebook社交圈的约炮网站Bang with friends也迅速走红,它的价值也是消除了信息不对称,用户可以选择性地将自己的意愿披露给特定的朋友,不会泄漏隐私。

互联网诞生以来的最大功绩就是大规模扩大了信息市场,使得信息的流通更加畅通无阻,交易的成本戏剧化地降低,移动互联网的爆发更是加强了这一点。不过迄今为止,信息市场的潜力还远远没有被挖掘穷尽,我可以断言几年以内还会有成百上千家利用互联网解决信息不对称的创业公司诞生。随着量变的积累终将会发生质变,我们即将进入一个前所未有的信息通畅的时代。

如何处理C++构造函数中的错误——兼谈不同语言的错误处理

用C++写代码的时候总是避免不了处理错误,一般来说有两种方式,通过函数的返回值或者抛出异常。C语言的错误处理一律是通过函数的返回值来判断的,一般是返回0NULL或者-1表示错误,或者直接返回错误代码,具体是哪种方式没有统一的规定,各种API也各有各的偏好。譬如fopen函数,当成功时返回文件指针,失败时返回NULL,而POSIX标准的open函数则在成功时返回0或者正数,失败时返回-1,然后需要再通过全局变量errno来判断具体错误是什么,配套的还有一系列perrorstrerror这样的函数。

C++的错误处理方式

C++号称向下兼容C语言,于是就将C语言通过返回值的错误处理方式也搬了进来。但C++最大的不同是引入了异常机制,可以用throw产生一个异常,并通过trycatch来捕获。于是就混乱了,到底是什么时候使用返回值表示错误,什么时候使用异常呢?首先简单谈论一下异常和返回值的特点。

异常的优点

  1. 错误信息丰富,便于获得错误现场
  2. 代码相对简短,不需要判断每个函数的返回值

异常的缺点

  1. 使控制流变得复杂,难以追踪
  2. 开销相对较大

返回值的优点

  1. 性能开销相对小
  2. 避免定义异常类

返回值的缺点

  1. 程序员经常「忘记」处理错误返回值
  2. 每个可能产生错误的函数在调用后都需要判断是否有错误
  3. 与「真正的」返回值混用,需要规定一个错误代码(通常是0-1NULL

使用异常还是返回值

我的观点是,用异常来表示真正的、而且不太可能发生的错误。所谓不太可能发生的错误,指的是真正难以预料,但发生了却又不得不单独处理的,譬如内存耗尽、读文件发生故障。而在一个字符串中查找一个子串,如果没有找到显然应该是用一个特殊的返回值(如-1),而不应该抛出一个异常。

一句话来概况就是不要用异常代替正常的控制流,只有当程序真的「不正常」的时候,才使用异常。反过来说,当程序真正发生错误了,一定要使用异常而不是返回一个错误代码,因为错误代码总是倾向于被忽略。如果要保证一个以返回值来表示错误代码的函数的错误正确地向上传递,需要在每个调用了可能产生错误的函数后面都判断一下是否发生了错误,一旦发生了不可解决的错误,就要终止当前函数(并释放当前函数申请的资源),然后向上传递错误。这样一来错误处理代码会被重复地写好几遍,十分冗杂,譬如下面代码:

int func(int n) {
  int fd = open("path/to/file", O_RDONLY);
  if (fd == -1) {
     return ERROR_OPEN;
  }
  int* array = new[n];
  int err;
  err = do_something(fd, array);
  if (err != SUCCESS) {
     delete[] array;
     return err;
  }
  err = do_other_thing();
  if (err != SUCCESS) {
     delete[] array;
     return err;
  }
  err = do_more_thing();
  if (err != SUCCESS) {
     delete[] array;
     return err;
  }
  delete[] array;
  return SUCCESS;
}

对使用异常容易增加函数出口的指控其实是不成立的,因为即使使用返回值,这些出口也是免不了的,除非程序员有意或无意忽略掉,但异常是不可忽略的。如果你认为可以把判断错误的if语句缩写到一行使代码变得「更清晰」,那么我只能说是自欺欺人。

有些错误几乎总是可以被立即恢复(譬如前面所说的查找一个字符串不存在的子串,甚至都不能说这是一个「错误」),而且返回值本身就传递一定信息,就不需要使用异常了。

鉴于C++没有统一的ABI,并不建议在模块的接口上使用异常。如果要使用,就要把可能曝露给用户的异常全部声明出来,不要把其他类型的异常丢给用户去处理,尤其是内部状态——模块的使用者通常也不会关心模块内部具体是哪条语句发生错误了。

构造函数中的错误

有一个相当实际的问题是,如何处理构造函数的错误?我们都知道构造函数是没有返回值的,怎么办呢?通常有三种常见的处理方法,标记错误状态使用一个额外的initialize函数来初始化,或者直接抛出异常

合格的C++程序员都知道C++的析构函数中不应该抛出异常,一旦析构函数中的异常没有被捕获,整个程序都要被中止掉。于是许多人就对在构造函数中抛出异常也产生了对等的恐惧,宁可使用一个额外的初始化函数在里面初始化对象的状态并抛出异常(或者返回错误代码)。这样做违背了对象产生和初始化要在一起的原则,强迫用户记住调用一个额外的初始化函数,一旦没有调用直接使用了其他函数,其行为很可能是未定义的。

使用初始化函数的惟一好处可能是避免了手动释放资源(释放资源的操作交给析构函数来做),因为C++的一个特点是构造函数抛出异常以后析构函数是不会被调用的,所以如果你在构造函数里面申请了内存或者打开了资源,需要在异常产生时关闭。但想想看其实并不能完全避免,因为有些资源可能是要在可能产生错误的函数调用过后才被申请的,还是无法完全避免手工的释放。

标记错误状态也是一种常见的形式,譬如STL中的ifstream类,当构造时传入一个无法访问的文件作为参数,它不会返回任何错误,而是标记的内部状态为不可用,用户需要手工通过is_open()函数来判断是否打开成功了。同时它还有good()fail()两个函数,同时也重载了bool类型转换运算符用于在if语句中判断。标记状态的方法在实践中相当丑陋,因为在使用前总是需要判断它是否「真的创建成功了」。

最直接的方法还是在构造函数中抛出异常,它并不会向析构函数中抛出异常那样有严重的后果,只是需要注意的是抛出异常以后对象没有被创建成功,析构函数也不会被调用,所以应该自行把申请的资源全部都释放掉。

如何在构造函数中捕获异常

构造函数与普通函数有一个很不一样特性,就是构造函数可以有初始化列表,例如下面的代码:

class B {
 public:
  B(int val) : val_(val * val) {
  }
 private:
  int val_;
};

class A {
 public:
  A(int val) : b_(val) {
    a_ = val;
  }
 private:
  int a_;
  B b_;
};

以上的代码中A的构造函数的函数体的语句在执行之前会先调用B的构造函数,这时候问题在于,如果B的构造函数抛出了异常,A该如何捕获呢?一个迂回的做法是在A中把B的实例声明为指针,在构造函数和析构函数中分别创建和删除,这样就能捕获到异常了。不过,实际上是有更简单的做法的。下面我要介绍一个C++的很不常见的语法:函数作用域级别的异常捕获。

class B {
 public:
  B(int val) : val_(val * val) {
    throw runtime_error("wtf from B");
  }
 private:
  int val_;
};

class A {
 public:
  A(int val) try : b_(val) {
    a_ = val;
  } catch (runtime_error& e) {
    cerr << e.what() << endl;
    throw runtime_error("wtf from A");
  }
 private:
  int a_;
  B b_;
};

注意上面A的构造函数,在参数列表后和初始化列表前增加了try关键字,然后构造函数就被分割为了两部分,前面是初始化,后面是初始化时的错误处理。需要指出的是,catch块里面捕获到的异常不能被忽略,即catch块中必须有一个throw语句重新抛出异常,如果没有,则默认会将原来捕获到的异常重新抛出,这和一般的行为是不同的。例如下面代码运行可以发现A会将捕获到的异常原封不动抛出:

class A {
 public:
  A(int val) try : b_(val) {
    a_ = val;
  } catch (runtime_error& e) {
    cerr << e.what() << endl;
  }
 private:
  int a_;
  B b_;
};

这种语法是C++的标准,而且目前已经被所有的主流C++编译器支持(VS2010、g++ 4.2、clang 3.1),所以几乎不存在兼容性问题,大可放心使用。

其他语言中的错误处理

Java倾向于大量使用异常,而且还把异常分为了两类分别是检查型异常(Checked Exception)和非检查型异常(Unchecked Exception),检查型异常就是java.lang.Exception的子类,用于报告需要检查的错误,也就是正常的业务逻辑,错误主要是由用户产生的,方便恢复或给出提示,譬如打开不存在的文件。而非检查型异常则是真正的系统异常,通常由软件缺陷导致,如数组下标越界、错误的类型转换等,这类异常继承于java.lang.RuntimeExceptionjava.lang.Error

Python和Java一样也倾向于使用异常,并不一定真的发生故障才抛出异常,譬如字符串转换为整数,如果字符串不合法,Python会抛出一个ValueError异常。甚至Python的迭代器在调用next()时没有更多的结果时会抛出StopIteration 异常。这是典型的用异常来处理正常控制流的方法,在Python中被广泛使用。按照优秀C++代码的标准来看,这是典型的对异常的滥用,既复杂又有额外开销,不推荐使用,但在Python中这是一个广泛遵循的约定。

相较于Java和Python,Go的错误处理是另一个极端,Go语言则根本没有异常的概念,而是普遍采用返回值的方式来表示错误,同时还提供了panicrecover语法。由于Go有多返回值的特性,避免了错误代码占用返回结果的弊端,所以你可以经常看到函数的最后一个返回值是error类型。由于总是用返回值传递错误,你可以看到Go代码中耦合了大量的错误处理,几乎再每条函数调用语句之后都有一个判断错误是否发生的语句。panicrecover机制十分类似于异常,程序在遇到panic时会一层一层退出调用栈,直到遇到recover。不过recover只在defer中定义,相当于一个函数只有一个recover,而且被recover恢复后会回到错误发生处继续向下执行代码。Go语言倾向于把一般错误都作为返回值传递,除非是非常可怕的、除了重置状态几乎无法恢复错误才会被panic语句抛出。

Go语言的recover机制和异常比起来,反倒更像Visual Basic语言中的On Error GoTo labelResume语法。这是一种非结构化的错误处理方式,具体是当声明有On Error GoTo label的函数发生错误以后,会调转到对应的行号,如果再遇到了Resume语句就会返回发生错误的语句后面的一条继续执行,例如下面这段代码:

Sub ErrorDemo
    On Error GoTo ErrorHandler
    Dim a as Integer
    a = 1/0 ' An error occurs.
    Print a ' Go back here
    Exit Sub

ErrorHandler:
    ' Code that handles errors.
    Resume
End Sub

Visual Basic中还有On Error Resume Next这样的万能错误处理语句,即遇到错误以后直接忽略并继续执行,这是一种非常危险而且不负责任的做法,但却可以在早期的Visual Basic代码中到处看到。事实上用返回值传递错误代码的时候许多人也并不处理而是直接忽略,这跟On Error Resume Next本质上没有什么区别,却比On Error Resume Next危害更大——因为On Error Resume Next至少还有个标记说明「老子就是这么不负责任」,但忽略错误返回值就难以被一眼发现了。

参考阅读

用Go语言计算PageRank

PageRank是搜索引擎结果排序的重要算法,其依赖的方式是链接结构分析,大致解释就是一个网页A有一个指向另一个网页B的链接,就相当于A给B投票,获得投票越多的网页的PageRank值越高。并不是每个网页的投票权重都是一样的,自己PageRank越大的网页投票权重越大,所以PageRank的计算公式是递归的,需要迭代计算,直到结果收敛。

我使用Go语言对真实网页的数据WT2g进行了PageRank的计算,计算出的结果分布如下图:

PageRank Distribution

观察发现,PageRank的分布服从齐普夫分布(Zipf Distribution),其中32%的网页的PageRank为最小值9.459×10^-7,超过一半的网页的PageRank的值小于6.600×10^-6,而PageRank的最大值为1.885×10^-3。

值得一提的是Go语言,推荐一个对Go语言特性的介绍:Go在Google:以软件工程为目的的语言设计。使用Go语言最大的感受是它的函数可以有多返回值,而且在各种API中这个特性被大量使用,而且约定多返回值的最后一个参数是error类型,表示是否有错误发生。这种错误处理的方法和C++、Java、Python、JavaScript使用的异常不同,倒是与C语言的错误处理相似。C语言习惯于把函数的返回值作为「是否有错误发生」的标记,如果有错误再通过其他的手段(如全局变量error)来获取,Go语言直接把错误作为了一个返回值。Go语言还支持一等函数(First Class Function)和闭包,因此方便用来实现yield功能,下面代码中的lineReader函数就是返回了一个生成器,用来按行读取文件,每调用一次读取一行,读完以后释放内存。Go语言还是一个显式有指针的语言,同时也提供了垃圾回收,省去了手动维护内存的麻烦。

以下是用Go语言计算PageRank的代码:

package main

import (
    "bufio"
    "errors"
    "fmt"
    "io"
    "math"
    "os"
    "strings"
)

type vertex struct {
    inDegree  int
    outDegree int
    pagerank  float64
}

type edge struct {
    start int
    end   int
}

var vertexs []vertex
var edges []edge
var vertexID map[string]int = make(map[string]int)
var numVertex int = 0

func lineReader(filename string) (func() (string, error), error) {
    f, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    buf := bufio.NewReaderSize(f, 64)
    return func() (string, error) {
        line, isPrefix, err := buf.ReadLine()
        if err != nil {
            if err == io.EOF {
                if err := f.Close(); err != nil {
                    return "", err
                }
            }
            return "", err
        }
        if isPrefix {
            return "", errors.New("buffer size to small")
        }
        return string(line), nil
    }, nil
}

func addVertex(vertexName string) int {
    var ID int
    var ok bool
    if ID, ok = vertexID[vertexName]; !ok {
        ID = numVertex
        vertexID[vertexName] = ID
        vertexs = append(vertexs, vertex{})
        numVertex++
    }
    return ID
}

func read() {
    readline, err := lineReader("wt2g_inlinks.source")
    if err != nil {
        panic(err)
    }
    for {
        line, err := readline()
        if err != nil {
            if err == io.EOF {
                break
            }
            panic(err)
        }
        // Line format is like "ID1\tID2"
        sections := strings.Split(line, "\t")
        if len(sections) != 2 {
            panic(errors.New("Illegal line format"))
        }
        start := addVertex(sections[0])
        end := addVertex(sections[1])
        edges = append(edges, edge{start, end})
    }
}

func calcPagerank(alpha float64, numIterations int) {
    // Initialize out degree of every vertex
    for i := range edges {
        edge := &edges[i]
        vertexs[edge.start].outDegree++
        vertexs[edge.end].inDegree++
    }
    var I = make([]float64, numVertex)
    var S float64
    for i := 0; i < numVertex; i++ {
        vertexs[i].pagerank = 1 / float64(numVertex)
        I[i] = alpha / float64(numVertex)
    }
    // Calculate pagerank repeatedly until converge (numIterations times)
    for k := 0; k < numIterations; k++ {
        for i := range edges {
            edge := &edges[i]
            I[edge.end] += (1 - alpha) * vertexs[edge.start].pagerank / float64(vertexs[edge.start].outDegree)
        }
        S = 0
        for i := 0; i < numVertex; i++ {
            if vertexs[i].outDegree == 0 {
                S += (1 - alpha) * vertexs[i].pagerank / float64(numVertex)
            }
        }
        for i := 0; i < numVertex; i++ {
            vertexs[i].pagerank = I[i] + S
            I[i] = alpha / float64(numVertex)
        }
    }
}

func main() {
    read()
    calcPagerank(0.15, 30)
    fmt.Println("Done")
}

BYVNotes是一个我用Go语言实现的简单在线记事本网站,使用了Revel框架。

美国之行(四)我对美国的印象

湾区与纽约

本来想写把标题成「西部与东部」,想想太大了,又改成「西海岸与东海岸」,还是太大了,我只去过两个地方,怎么可以概括呢?于是只好只说一说我对湾区和纽约的印象吧。

我对湾区的印象非常好,这里科技发达、气候宜人,人们对外来者普遍友善、开放,你几乎不会感受到对外来者的歧视,因为大家都是外来者。甚至如果你不懂英文,那也没关系,因为这里有美国最大的华人社区。硅谷更是创业圣地,是追逐梦想的地方。要说不足之处,恐怕要数缺乏公共交通了,湾区人口相对分布分散,因此没有像纽约、北京那样发达的地铁公交系统,许多地方没有汽车很难去。

我刚来到湾区的时候,过马路都不习惯,因为汽车远远地看到行人就会放慢速度停下来。但是我在天朝生活惯了,见了汽车以后还是不敢过,于是便上演了好几次我傻站在路边等车过,车停在路口等我过。

纽约让我看到了美国的另一面。纽约的汽车是不怎么让行人的,行人闯红灯也非常普遍,人和人之间互相不看一眼,不像在伯克利街上还有陌生人给我打招呼。纽约的道路没有湾区干净,街边还有买汉堡热狗的小摊,和国内卖煎饼果子鸡蛋灌饼的有一拼。纽约冬天的气候很寒冷,跟北京真的有不少相似之处——都在东部靠近海岸地区,而且穿过北纬40度线。纽约的人口密度很大,平均每平方公里有一万人,这个密度和北京差不多。

看看纽约街头的「美国式过马路」:

Jaywalk

治安

我去美国之前就听说了美国许多地方治安不好,到了伯克利以后更是就被人警告晚上不要出门,否则不安全。这个建议一开始一直让我将信将疑,白天看起来这么漂亮安静平和的小镇,难道晚上就变得凶险无比?我在北京晚上从来没有过不敢出门的经历,除非是外面刮沙尘暴。然而没过两天一件事情就发生了,一天晚上我听到外面有许多警车呼啸而过,而且更可怕的是我住的旅馆的门口竟然躺着一个黑人,我一开门,他看了我一眼,然后没有理我。第二天听说伯克利南部不远的奥克兰发生了枪击事件,于是奥克兰这个名字就深深地印在了我的脑中。

后来见到一个Google的朋友,他也给我讲了一个故事,说是一个工程师来面试Google,被录用以后特别高兴于是当晚就去奥克兰找一个朋友出去狂欢,然后两个人就在停车场被枪杀了,原因是劫匪嫌他身上现金太少。再后来听说奥克兰房价特别便宜,因为人都搬走了,尤其是见不到年轻人了。奥克兰究竟是什么地方?查过才知道,它是湾区的三大城市之一,与旧金山、圣荷塞并列。然而奥克兰却被列在2008年国会调查报告的全美最危险的城市排行榜中,名列第五。奥克兰似乎从前并不是这样,否则它不会成为湾区第三大城市,我看到李小龙曾经都在奥克兰开过武馆。

奥克兰究竟为什么会变成这个样子?我了解到的一种说法是,旧金山湾最大的装卸货港口就在奥克兰,它是一个连接了海运与铁路的交通枢纽,因此有大量的港口工人。这些港口工人大多是黑人或者拉美人,所以工资较低,教育水平也较低,而且容易受到经济波动的影响。2008年经济危机以来,大量工人失业,于是走上了犯罪的道路,因此治安变不断恶化。年轻的有能力的人纷纷搬离了奥克兰,剩下的都是年迈的或者穷人,于是便陷入恶性循环中。

事实上美国的不同阶层不同人种是习惯于聚居的,譬如总体来说湾区有钱人喜欢住在旧金山或硅谷,而穷人聚集在东湾,从房价上就可以明显看出区别。就算是在奥克兰这种地方,社区直接还是有不少区别的,这个链接展示了这一点:美国加州奥克兰市的各区房价跟主要聚居人种的关系(图)

纽约的治安据说是美国大都市中最好的,除了上城黑人区外,其他地方大多是挺安全的。于是我在纽约的中城附近就敢晚上一个人出去,因为这边许多商店好像不关门一眼,十二点还在热卖,在湾区许多商店晚上七八点就打烊了。纽约是货真价实的「不夜城」,连地铁都是24小时运转的。

收入与物价

在国内一直有传言说中国的物价已经超英赶美了,而收入却还相差千里,真的是这样嘛?我觉得这句话至少一半是对的,收入的确相去甚远,2011年美国劳工部宣称美国人的平均年收入37000美元左右,按照汇率换算到人民币这可不是一个小的数目,在国内能拿到这个工资的绝对是高收入群体了。37000美元只是平均工资,在硅谷,一个工程师拿100000美元的税前收入是稀松平常的一件事,在纽约的金融圈中就更高了。但问题是美国的一般人如何呢?37000美元是平均工资,由于分配的不平衡性,中位数肯定要低于这个数目。

说完收入再来看看物价,先看看超市里面的普通生活用品,食材、日常用品这些东西不算贵,价格按照汇率换算到人民币也就比国内高50%到200%左右吧,从平均购买力来说就是国内二到三倍。但涉及到服务的价格这部分就贵了,差不多1美元在美国当1人民币在中国花。而感觉最便宜的是电子产品了,价格和国内一样,甚至比国内更便宜,算上汇率就是高六七倍的购买力,怪不得美国科技发达呢。

Bestbuy

再来看看房价呢?美国尽管地广人稀,房价却差异巨大,便宜的有底特律的10美元的房子,昂贵的则高达几百万美元,简而言之就是关键看房子在哪。拿湾区来说,有10万美元左右的房子,普通劳动者是绝对买得起的,只要愿意买,地点就在奥克兰。当然也有标价近千万美元的学区房,地点在Palo Alto,旁边就是斯坦福大学。看看中国,北京五道口已经有了每平方米超过10万人民币的房子了,毕竟靠近清华嘛。普通人买得起的便宜的房子呢?恐怕到四线城市去也难以找到吧,但如果愿意去乡镇还是有的。

当然以上这个物价的比较考察很不全面,只是个人感觉而已,况且湾区、纽约都算美国物价比较贵的地方,结果就权当参考了。总之说中国的物价赶上美国不是没有道理。

轶事——电脑损毁

在从旧金山到纽约的飞机上,我正坐得好好的,空乘服务员来倒水,坐在我右边的一个人把杯子伸手放在我电脑的上空,于是就把水倒到了我的电脑上。电脑屏幕就突然黑了,我心想这下完了,没电脑自己一个人到纽约可怎么活啊。我身边的这个美国人也一副惊慌失措的样子,她表示非常抱歉。我们谈了几句,她决定赔我钱,先把身上的二百元现金都给了我,然后说等到纽约去看看维修要多少钱。

就这样我们就聊了起来。她看起来有四五十岁,是一个教英语的老师,曾经在小学、中学和大学都教过,她的祖父是斯坦福大学的教授。她和我讲美国的基础教育十分失败,有人上完小学了还无法阅读文章。我告诉她中国的党化教育和言论管制,以及严酷的出版审查,她感觉很惊异,不敢相信这到现在还是存在的。她对中国的发展十分好奇,而且谈吐间流露出一种恐惧感。她说美国国家庞大的债务都是借自中国的,而美国有些人什么也不干就靠社会福利过日子,这些钱都是美国政府从中国借来的,而且美国政府就没准备还。她还指名道姓地骂了奥巴马不少坏话,看得出她是一个奥巴马的反对者,而且很有可能是共和党人。

下飞机前我们互相留了联系方式。第二天我到大中央车站的苹果维修中心,一问才知维修费竟然要1400美元,我心想这在天朝1400人民币都用不了,于是就不修了。给一个朋友打电话,他建议我给苹果要维修价格认定书,然后把这个文件给那个损毁我电脑的人。我就这么做了,那个人看了以后二话没说就给了我一张支票,然后我回国以后花了1000人民币修好了。

这个经历实在令我难忘,许多人告诉我说要是这件事发生在中国,损毁我电脑的人肯定千方百计推卸责任,而且一旦下飞机以后肯定会杳无音信。而这位美国人却非常讲信用,而且实际上这件事的责任并不在她一个人,那个不专业的空乘服务员也是重要的责任人,她无论如何不应该在我电脑的上空倒水。她自己一个人承担了所有的责任,实在不得不令我佩服。后来了解到她住在湾区的Mill Valley,属于北湾,而北湾一般是富人的区域,所以这一千多美元可能对她来说真的不算什么吧。其实美国人并不比中国人天生要讲信用,关键看讲信用和不讲信用哪个成本更低,换句话说就是有没有钱。

美国梦

我在湾区时候有幸被一个朋友带着去Palo Alto的私人机场看了看,而且尝试了一把乘坐私人飞机。私人机场就在Palo Alto东部靠海的地方,里面停着至少几百架私人拥有的飞机,机场与加油站和跑道,还有几个飞行俱乐部。我被带着飞上了5000英尺,由于空气很好,可以看见很远的地方。

Private Aircraft

千万别以为这是超级富豪的游戏,我的那位朋友也只是一个公司的普通工程师而已。私人飞机的价格低廉得让你惊讶,我一开始听到几乎是不敢相信的这是真的,我算了算看努力工作几年是买得起的,而且飞行、养护的成本也不是不可接受。对我来说,这种感觉不仅仅是飞上天那一瞬间的兴奋。更多的是我感受到了什么叫美国梦,什么叫只要努力就能实现你的梦想。就像著名的华裔建筑师贝聿铭在80年前来到美国的时候,彻底被汽车吸引了,他竟然发现在中国只有显赫的达官贵人才能坐得起的汽车,在美国竟然如此普遍,那正是他的美国梦诞生的时刻。

引用杨哲的这篇文章

这里是Palo Alto机场,是一个public airport,里面停着的都是私人飞机。旁边有一些飞行俱乐部。可以向俱乐部租飞机飞,也可以请教练教开飞机。只要有驾照,开飞机不需要向任何部门申请,只要跑道排好队,想什么时候飞,向哪里飞,都随意。只要是公共机场,跟地面联系一下排个队就可以降落。公共机场就像停车场一样普遍和自然。从家里来机场,只要把车停在自己飞机的机位,把飞机拉出来,回来了再开车回家,简单高效。当然,基本上是个正常人都能学会开飞机,考到驾照,以及开飞机。

在Palo Alto机场逛的时候,我才意识到,那个FG S11E09其实并不是在刻意输出价值观,这就是美国的生活、美国梦。这是一个神奇的国家,真的都像是假的。

(完)

美国之行系列

美国之行(三)纽约

纽约

早上10点出发,从旧金山飞了四个小时才到纽约,但是却已经是晚上7点了,是因为有时差的缘故,美国东部比西部要晚3个小时。以前在国内从未有这种感觉,原因是在国内总是大致南北飞,没有横贯东西4000公里的经历,何况中国就一个北京时间,哪怕是到新疆也一样。

下飞机以后,感觉一瞬间又回到了冬天,纽约很冷,零下好几摄氏度,和北京差不多,一点也没有旧金山那种宜人的感觉。肯尼迪机场在皇后区,距离曼哈顿挺远的,要坐地铁去。从机场出发要坐地铁首先要坐SkyTrain,SkyTrain很有意思,上车是免费的,我还以为真的免费,但却发现收费在出站口,进去容易,想出来就得付钱了,真是考虑周到。纽约地铁和湾区的按站计费不一样,到哪里都是2美元一次,和北京颇为类似,都是两块钱。进入纽约地铁后,第一感觉就是令人震惊的脏乱差,轨道内都是垃圾,上面还漏水,而且设施破旧,站台上人一旦少了颇有恐怖片的氛围。跟天朝的地铁比起来,美国的真是弱暴了。不过区别在于,像北京的地铁大多是最近十年迅速建成的,而纽约地铁已经快有一百年的历史了。

New York City Subway

纽约是美国最著名的城市,去美国不能不去纽约。它还是美国人口最多的城市,有超过八百万人(跟天朝比其实不算多)。纽约市在纽约州的东南角,包含五个区,分别是曼哈顿岛、皇后区、布鲁克林区、布朗克斯区和史泰登岛。其中最著名的就是曼哈顿岛了,纽约的几乎所有名胜全部都在曼哈顿岛上。

曼哈顿

学过计算机的一定不会没有听说过「曼哈顿距离」,究竟为什么叫曼哈顿距离呢?看看曼哈顿地图就明白了。曼哈顿岛上的道路惊人的整齐,整个到被分割为一个个大小几乎相等的矩形街区,就连道路的命名都极其规则。东西方向的道路差不多一律被命名为街(Street),从南向北依次是1st Street, 2nd Street, 一直到220 Street。南北向的道路从东向西依次被编号为1st Avenue一直到12th Avenue,但中间有几个数字被跳过了,换成了名字。所以在曼哈顿再没有方向感的人也会有方向感,再不知道东西南北的人也会在脑中建立起罗盘。

曼哈顿由南向北大致可以分为下城、中城、上西城、上东城。其中下城有著名的自由女神、华尔街、911世贸中心原爆点和唐人街,中城有帝国大厦、时代广场、百老汇剧院,上城有中央公园、大都会博物馆、哥伦比亚大学。据说纽约的中城和下城比较繁华也比较安全,而上城北部哈莱姆区是纽约的黑人区,比较贫困。我在纽约的几天住在34街与第3大道附近,算是曼哈顿中城比较好的地区。

中城

我在纽约的第一站是中城,从34街出发,一边走一边看着纽约街头的风景。曼哈顿的每个街区都是一栋摩天大楼,走在其间有一种十分渺小的感觉。

New York City Midtown Building

我不是第一次见到高楼大厦,但纽约的高楼大厦和北京上海的很不一样,想来想去我发现主要的差别在于纽约的摩天大楼特别密集,而且道路很窄。这种现代风格的建筑给我一种强烈的感官刺激,让我十分兴奋。

时代广场

步行二十分钟就走到42街与第7大道的路口,就看到了传说中的时代广场。时代广场曾经是纽约时报的所在地,但现在是聚集纽约的剧院、音乐厅、购物中心、酒店的集中地。时代广场给人最大的感受就是它四周无孔不入的巨型广告宣传版,堪称商业文化的世界中心。

Time Square

Time Square

后来看照片时才注意到当时身后的「The Phantom Of The Opera」的广告,当时没有去看实在是太可惜了。

Time Square

大中央车站

从时代广场向东走十几分钟,就看到了摩天大楼之间的大中央车站。大中央车站汇集了纽约的好几条重要的地铁和铁路,是纽约的交通枢纽之一。大中央车站建于1913年,我去看的时候恰好是100周年纪念。

Grand Central Terminal

大中央车站除了是一个交通枢纽以外,也是一个商业中心,苹果公司就在大中央车站租了很大一块地方销售iPhone、iPad和Mac。大中央车站的地下有各国的风味小吃,候车者可以在这里休息。

帝国大厦

到纽约最不能错过的地方除了自由女神,恐怕就是帝国大厦了。尽管帝国大厦不是纽约最高的建筑,却是纽约的地标建筑,因为它一度一直是美国最高的建筑,而且建造从1930年开始到1931年建成仅仅用了一年零两个月,堪称一个奇迹。

说来也巧,我去美国之前正好看了三部电影,分别是「西雅图夜未眠(Sleepless in Seattle)」、「金刚(King Kong)」和「印度英语(English Vinglish)」,这三部电影都出现了帝国大厦,所以当我爬上帝国大厦的时候,激动的心情是难以言表的。回来以后正好又看了「北京遇上西雅图」,重温了熟悉的场景。

Empire State Building

Empire State Building

Empire State Building

帝国大厦不仅外表宏伟,内部也十分精致。我花了27美元的门票登了上去,从游览入口进去,发现路线都是精心规划了的,过一个检查点的时候还被拍了一张照片,我以为是安全检查。上去以后先到了80楼,然后上到了86楼的观景台,看到了几乎整个曼哈顿的景色,下面高楼大厦林立,真的是现代化大都市,心情十分舒畅。下去的时候,发现有一面墙全是照片,而且竟然看到我的照片也被打印出来了,效果还不错,工作人员说这个卖20美元。在国内类似的也有,不过都是按需打印的,像这里这样直接帮你打印好,还真是第一次见。想想我真心佩服经营者的智慧,打印一张照片的成本并不高,批量化还能继续降低成本,利用这种方法可以让顾客一眼看到效果,同时还利用了中间观景的时间,相信不少人会愿意花这个钱买。

自由女神

自由女神是纽约乃至美国的象征,曼哈顿南端自由岛上的自由女神像可谓是纽约最热门的景点了。只可惜由于去年的「桑迪」飓风,自由女神像遭到损毁,所以自由岛不能登上了,只能在海上看。

Statue of Liberty

华尔街

看完自由女神后,步行不远就是华尔街。华尔街在外表看真的可以说是其貌不扬,至少和想像中的那种纸醉金迷颇有不同。看来这些大型金融机构还是很低调的,只是名声在外,隐瞒不了。而且传说中的「华尔街金牛」根本不在华尔街,而在华尔街北边圣三教堂的旁边。

Bull

唐人街

纽约唐人街在下城,靠近熨斗区。走进纽约的唐人街有一种回到中国的感觉,不仅仅是因为随处可见的中文照排,还有脏乱的街道。唐人街看起来很贫穷,超市的东西也便宜,简直和中国的超市差不多,国货应有尽有。之后我还想去看小意大利,可是走到了地图上的小意大利,却发现招牌还都是中文,看了真的被唐人街侵占了。

Chinatown

大都会艺术博物馆

大都会艺术博物馆是纽约最有名的博物馆,据说馆藏超过二百万件来自全世界各地的珍贵艺术品。我在大都会博物馆里面逛了八个小时,浏览了十几个展区的几百个房间,只能说是浏览,因为这个博物馆真的太大了,仔细逛的话一个月都看不完。况且纽约还不止一个艺术博物馆,纽约真的是一个艺术爱好者的圣地。

Metropolitan Museum

中央公园

中央公园是曼哈顿上城的一大片绿地,大都会博物馆和自然历史博物馆就在中央公园旁边。纽约的中央公园有点像旧金山的金门公园,都是城市中央的一个特大的公园,而且是一个矩形,区别在于一个是横的一个是竖的。中央公园的环境十分优美,而且风景和周边城市建筑非常和谐,这和中国很多风景区看到的远处丑陋的建筑颇有不同。

Central Park

哥伦比亚大学

哥伦比亚大学在中央公园的西北边的晨边高地,靠近哈莱姆区。哥伦比亚大学和斯坦福、伯克利这些西部的名校比起来,校园规模简直不是一个数量级,它只有五个街区那么大,毕竟是在曼哈顿这种寸土寸金的地方。但是哥伦比亚大学绝对是美国最牛逼的几所大学之一,它的历届毕业生和教职员中共有97名诺贝尔奖得奖者,于世界各大学中排名第一。在学术界以外它的影响力也很强,绝对是世界超一流水平。

Columbia University

哥伦比亚大学所处的位置十分微妙,它就在黑人区的边缘地带,处于1960年代「垮掉的一代」运动发源地,因此周围有不少爵士乐酒吧、咖啡厅,但因此治安并不是很好,晚上是不建议独自在街上走动的。

世贸中心九一一原爆点

世贸中心双子塔曾经是纽约最高的建筑,是一个巨型的商业中心。但是在2001年9月11日,它被基地组织公布袭击摧毁了。九一一事件成为近十几年来美国最严重的事件,也是历史上美国本土遭受的最严重的袭击,死亡人数超过二战时期的珍珠港事件。

现在,九一一的原爆点已经重建多年,建成了九一一纪念广场,原来双子塔的塔基位置现在成为了两个巨型的水池,水池周边上刻有所有罹难者的姓名,供世人凭吊。广场中还有一个尚未开放的博物馆,预计半年以后会开放。在世贸中心遗址旁边还在建设一座新的摩天大楼,名为新的世贸中心一号大楼,又名自由塔(Freedom Tower),从2006年开工,预计2013年7月完工。自由塔建成以后将会成为新的北美第一高塔,象征着美国在废墟上重建家园,与恐怖主义对抗到底的精神。

World Trade Center

Google

Google当然不是一个公开的景点,但是作为Google的员工(实习生),我不能不去纽约的Google看看。纽约的Google在中城下城之间的熨斗区,大楼整整占了一个街区,当然这栋大楼里面不仅仅是Google,还有许多其他的公司,据说这栋大楼里面直接接入了大西洋海底光缆,因此许多通信公司也进驻了这个大楼。

Google NYC

Google纽约办公室和全世界的Google一样都十分漂亮,办公室的环境非常轻松,也有许多有特色的东西。譬如办公室内的路标,全部都是纽约地铁风格的。还有楼层与楼层之间有梯子可以直接爬上去,省去了走楼梯的麻烦。

美国之行系列