明镜止水

知行合一

统计

留言簿

阅读排行榜

评论排行榜

为什么重载逻辑运算操作符不支持短路求值,用函数实现IF的语义错误

IF 家族的分支语句,在计算机程序设计中可以说必不可少。相信读者都很熟悉这种 IF 结构

IF 条件 THEN
    一些语句;
ELSE
    另一些语句;
END IF

这是从 ALGOL 语言一脉相承下来的,很“自然”的 IF 写法。而早期的 FORTRAN 的 IF 写法却不这么直观,而是

IF (表达式) A B C

取决于表达式的值是小于零,等于零还是大于零,分别跳到(等价于 goto)标签 A, 标签B 或者标签 C。这个 IF 隐含了三个 Goto,可以说和结构化编程的实践截然相反,降低了程序的可读性。 Fortran 首创的这个三分支跳转的 IF 饱受诟病,Fortran 77 开始支持结构化的 IF,而 Fortran 90 标准进一步宣布三分支跳转的用法已经“过时”,不支持使用。

用 XIF 子程序完成类似于 IF 的分支功能,用法是:

XIF(条件, 表达式A, 表达式B)

取决于条件的满足与否,XIF 返回表达式A 或者表达式B 的值。很快,他发现,用子程序的方法实现 XIF,在语义上并不正确。我们知道,在 Fortran 和其他高级语言中,函数参数的值在进入函数之前必须全部确定。在 XIF 这里,不难看出,不管条件满足与否,我们都先要计算表达式A 和表达式B 的值。而 IF 是个分支逻辑,从语义上来说,应该只计算满足条件的分支的值。因此,用函数来实现 IF 是不正确的 [b]。

作为一个旁注,尽管 John McCarthy 早在50多年前就发现了函数实现 IF 是语义错误的,现代的程序员还常常犯这个错误。一个值得一题的例子是 C++ 逻辑运算符重载和短路表达式的不等价性。我们都知道,在 C 语言中,逻辑与 (&&) 和逻辑或( || ) 都隶属于短路表达式,也就是说,对于 A && B 这样的表达式,如果 A 已经确定为 false,就无需计算表达式 B 的值,即 B 的计算被”短路”。以 C 为蓝本的 C++ 一方便保留了这些短路表达式,另一方面在面向对象的特性中,引入了运算符重载。具体来说,只要一个对象定义了 operator&& 成员函数,就可以进行 && 运算。乍一看,这是一个很酷的特性,可以让程序员用 A&&B 这样的数学表达式表达复杂的逻辑关系。然而,仔细想想,  A.operator&&(B) 在语义上并不等价于 C 所定义的 A&&B,原因在于 A.operator&&() 是个函数,在求值之前需要先计算 B 的值,而后者是个短路表达式,本质上相当于

    IF A:
     return True
    ELSE:
     return B
    因为短路表达式不一定会对 B 求值,这两者从语义上就是不等价的。如果 B 不是一个简单的对象,而是一个复杂表达式的时候,对 B 求值可能有副作用,而这个副作用,是写 A && B 并把它当做短路表达式的程序员所没有预见的。按照 C++ Gotcha 的说法,这很容易造成潜在的程序 Bug。实际上,C++逻辑运算符重载是一个危险的特性,很多公司的编程标准都禁止使用逻辑运算符重载。

转自 http://blog.youxu.info/

posted on 2012-05-07 15:19 寒璿 阅读(386) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理