基于Algol的语言鼓励使用大括号的原因之一是鼓励在定界大括号之间添加更多行而不必移动大括号。也就是说,如果以
if (pred)
{
printf("yes");
}
很容易出现并在花括号内添加另一个语句:
if (pred)
{
printf("yes");
++yes_votes;
}
原来的表格是
if (pred)
{ printf("yes"); }
那么我们就不得不“移动” 两个大括号,但是我的示例更关心后者。在这里,花括号界定了打算作为语句序列的对象,这些语句主要是出于副作用而调用的。
相反,Lisp缺乏陈述;每种形式都是表达式,产生一些值-即使在极少数情况下(例如Common Lisp),也有意通过空(values)
形式将该值选择为“无值” 。与嵌套表达式相反,查找表达式序列不太常见。不再经常出现“打开一系列步骤直到结束定界符”的愿望,因为随着语句消失并且返回值变得更常见,因此忽略表达式的返回值变得更加罕见,因此更多很少评估仅针对副作用的表达序列。
在Common Lisp中,progn
表单是一个例外(及其同级):
(progn
(exp-ignored-return-1)
(exp-ignored-return-2)
(exp-taken-return))
在此,progn
按顺序计算三个表达式,但丢弃前两个表达式的返回值。您可以想象在其最后一行上写上最后一个右括号,但要再次注意,由于最后一种形式在这里是特殊的(尽管不是普通Lisp的特殊含义),并且经过独特的处理,很可能会添加新的表达式中间的表达式,而不仅仅是“在末尾添加另一个”,因为调用者不仅会受到任何新的副作用的影响,而且还会受到返回值可能发生变化的影响。
简而言之,Lisp程序的大多数部分中的括号是分隔传递给函数的参数(就像使用C语言一样),而不是分隔语句块。出于相同的原因,我们倾向于使包围C中函数调用的括号保持在参数附近,因此在Lisp中也是如此,而没有动力偏离该紧密分组。
括号的关闭远不如括号的打开形式重要。随着时间的流逝,人们学会了忽略括号并按形状进行读写,就像Python程序员所做的那样。但是,不要让这种类比导致您认为完全删除括号是值得的。不,这是最适合的辩论comp.lang.lisp
。