维持形式语言精确性的权衡

当今的编程语言变得越来越以人为本, 程序员变得越来越边缘化。
这是好事, 计算机的目的就是为人类服务不是么?
(如果以后真出现AI种族, 请你们忽略上面的言论 …… )
编程, 就是让引导计算机协助人类自动无错的完成繁重的工作。

但是, 凡事都有, 都需要权衡

计算机能完成的工作, 绝大多数是形式化的, 因为计算机还不够智能。
而编程语言也是由计算机翻译执行的, 所以大多数也都是形式语言。
编程语言需要尽可能精确的无二义性的表达程序员的思想。
二义性一般来说, 就预示出现了错误。

所以,作为语言的设计者, 需要在“人本”与“精确”之间做出权衡。

下面是一些例子:

C++函数声明 : (爱之深则恨之切……)

一个《Effective STL》中的经典例子

ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile)
                 ,istream_iterator<int>() );

data能从dataFile中读出ints吗?
不能。
data甚至不是一个链表, 而是一个 —— 函数声明

在例子中添加:

data.clear();
list<int> result = data();

然后从编译错误中可以更清楚的看到这点, 它是一个函数声明。

C的声明向来以混乱闻名, C++为了兼容性, 当然也在所难免。
问题究竟出在哪里?

是因为函数声明中, 参数名可以省略吗?
如果不能省略参数名, 那么这个问题就不存在了。

但是这付出了代价, 函数参数列表中的形参声明,需要的仅仅是形参类型而非形参名。
(通常给出一个形参名是一个好习惯, 但也有不这样做的,比如MinGW的WinAPI头文件)
而且问题的实质,其实不在这里,而是以下原因:

参数名可以有两种(或以上)写法

如下语法已经可以很好的完成工作的情况下

int f(double d);
int f(double );  /* omit parameter's name */

为什么需要如下的语法:

int f(double (d) );
第二行作了同样的事情。名为d的参数左右的括号是多余的,被忽略

为什么需要这种多余的东西?
它没有带来任何实际的好处, 还需要多输入2个字符。

虽然可以把括号放在参数名左右这一点可能比较新。(在不久前我也觉得它新。)

连Scott Meyers 都觉得它很新, 可以说,很多人都会觉得它很新, 几乎没有使用过它。
所以, 将这种声明语法去掉代价是很小的。

如下语法也可以很好的完成工作

int g(double (*pf)());
int g(double (*)());  /* omit parameter's name */

为什么需要如下的语法:

int g(double pf());
int g(double ());    /* omit parameter's name */

正是因为有了不同的方法成同一件事 —— 给参数命名, 使得语言在这里变得不精确, 而导致了上述问题。
同时, 改进该语法, 代价并不是特别高昂 ——

int f(double (d)); /* 有人用吗? */
int g(double pf());/* 也许有人用,废除它的代价是多输入3个字符而已 */

既然提到了完成同一件事有不同的方法, 不得不提到另外一个例子:


Perl与Python中心思想对比

Perl 主张: TMTOWTDI

TMTOWTDI -- There's More Than One Way To Do It.
不止一种方法来做这件事。


这和 Python 截然相反。
Python的 格言 (The Zen of Python) 之一:

There should be one -- and preferably only one -- obvious way to do it.

类似的言论还有:

There is only one way to do it.
Explicit is better than implicit.

在这点上, 我是赞同Python的。
作为程序语言, 一味的追求接近自然语言(Perl的设计者是一位语言学家……)在目前是不切实际的。
形式语言需要的就是精确无二义性,和明言(明确而非隐晦表达编程人员的思想)。

Perl和Python在这个方面的分歧,决定了后者的程序被公认为易读、易维护,而前者的程序则获得 write-only 的“美誉”。


ECMAScript的分号 “;”

Loosely, if you break a line of code in such a way that the line before the break appears to be a complete statement, JavaScript may think you omitted the semicolon and insert one for you, altering your meaning.

将导致:

return true;

与如下代码具有未完全不同的含义:

return
true;

这是有可能发生的, 如代码自动格式整理, 不同编辑器直接的复制粘贴。
一旦发生, 必定是个错误, 同时又极难检查出。

避免这一歧义的代价并不高昂 : 语句必须以分号结束。
为了省略一个分号, 给错误留下机会真的值得吗?


Fortran与航天的传奇故事

传奇么, 版本肯定很多了 ……
我看的是《C专家编程》 27页上的版本。

简单的说, 一个程序员找到bug原因是如下语句:

Do 10 I = 1.10

它本应该是:

Do 10 I = 1,10

为什么编辑器不吱声? Fortran语言的哪些特性使得该错误通过了编译?

  1. Fortran中, 变量无需声明即可使用。
  2. Fortran中, 空白字符没有什么意义, 它们甚至可以在标识符内部出现。

因为上述原因, 导致错误的语句被理解为一个合法的语句:

Do10I = 1.10

将 1.10 这个浮点字面量, 赋值给 Do10I 这个变量 ……

废除特性1, 或者特性2, 编译器就能检查出这个错误。
而特性1是在许多语言中都有的, 如BASIC。
特性2 就不那么常见了, 但也是有历史因素的:
Fortran的设计者初衷是这样可以避免因打卡机的振动而产生错误, 提高程序的可靠性。

以现代的技术,对于特性2, 废除的代价应该不高吧?


总结:

这些例子都是因为语言在某些地方不够精确而导致了种种问题。
同时,改进这些不精确的地方并不需要付出很高的代价, 令其变得不“人本”。
当然, 有些语言已经存在很久了, 不可能改动。
这里说的改进是指如果重新设计一门语言, 应该从这些地方吸取教训。

posted on 2008-11-18 03:06 OwnWaterloo 阅读(87) 评论(3)  编辑 收藏
::...
免责声明:
当前网页内容, 由 大妈 ZoomQuiet 使用工具: ScrapBook :: Firefox Extension 人工从互联网中收集并分享;
内容版权归原作者所有;
本人对内容的有效性/合法性不承担任何强制性责任.
若有不妥, 欢迎评注提醒:

或是邮件反馈可也:
askdama[AT]googlegroups.com


点击注册~> 获得 100$ 体验券: DigitalOcean Referral Badge

订阅 substack 体验古早写作:


关注公众号, 持续获得相关各种嗯哼:
zoomquiet


自怼圈/年度番新

DU22.4
关于 ~ DebugUself with DAMA ;-)
粤ICP备18025058号-1
公安备案号: 44049002000656 ...::