大括号,那个标志着代码块开始和结束的熟悉符号,什么时候成为编程的一部分?也许更重要的是,代码块何时成为程序结构的一部分?在本文中,我将回答这些问题。
首先,以非块状语言为例
所有熟悉C风格编程语法的程序员都熟悉编程块,用C、C ++、Java、JavaScript和其他语言编写的所有主要编程结构都是块结构的。换句话说,这些构造的主体放置在块中,这些块由打开和闭合的花括号分隔。有人可能会说,这并不是完全正确的,因为 if
语句和循环(这里只提到两种编程构造),如果它们的主体只有一个语句,就可以不用块来构造。然而,在过去的几年里,编程风格专家建议将所有的构造体放在一个块中(参见Douglas Cockroft的“JavaScript: The Good Parts”中关于将所有if和循环体放在块中的论点特别有说服力)然而,第一批高级编程语言并没有块。我将使用大约在20世纪50年代末的FORTRAN语言来演示这一点。
function f(t) { return Math.sqrt(Math.abs(t) + 5 * Math.pow(t, 3));}let arr = [];let y;for (let i = 0; i < 11; i++) { putstr("Enter a number: "); arr[i] = parseFloat(readline());}for (let i = 10; i >= 0; i--) { y = f(arr[i]) if (y > 400) { print(y,"is too large."); } else { print(i,y) }}
不要担心程序做什么。重要的一点是,这个程序执行了Knuth认为在现代编程语言中对测试很重要的所有特性。正如我提到的,Knuth将几种编程语言与该算法进行了比较。他最后看过的一种语言,现在通常被认为是第一种真正的高级编程语言。下面是Knuth编写的TPK程序在Fortran中的样子:
C THE TPK ALGORITHM, FORTRAN STYLE FUNF(T) = SQRTF(ABSF(T))+5.0*T**3 DIMENSION A(11)1 FORMAT(6F12.4) READ 1, A DO 10 J = 1, 11 I = 11 – J Y = FUNF(A(I+1)) IF (400.0-Y) 4, 8, 84 PRINT 5, I5 FORMAT(I10, 10H TOO LARGE) GO TO 108 PRINT 9, I, Y9 FORMAT(I10, F12.7)10 CONTINUE STOP 52525
Fortran语法的细节我就不多说了,但你可以清楚地看到,该语言不包含块。函数定义在一行上,而不是作为代码块,DO
循环使用一个行标签来控制循环。当时的高级语言还没有将复合语句分组成块的概念,这些语言仍然依靠 goto
来控制程序流。该代码由Knuth使用1957年的Fortran版本编写。1957年至1960年间,一种新的语言ALGOL被开发出来,它弥补了当时Fortran等语言和其他高级语言的许多缺陷。Algol中的块Algol编程语言首次被描述是在1958年,尽管该语言最流行的版本是Algol 60。Algol的功能之一是将语句分组为复合语句(称为块)的能力,每个Algol程序都被视为一个块,因为一个程序通常包含一个或多个语句(因此为复合语句)。Algol的开发者认识到,许多编程场景,如条件和循环,需要将语句作为一个单元来考虑。Algol使用关键字 begin
和 end
标记了块的开始和结束。一个区块可以嵌套在另一个区块里面,外区块被认为是主导区块,内区块被认为是从属区块。例如,这里有一个Algol程序,可以嵌套块:
begin real a; a := 1; begin real a; a := 2; print(a) end; print(a)end
依次打印数字2和1。这是在Algol的 if
语句中如何使用块的示例:
if x > -1 then begin if x ≠ 0 then x := 1/x end;
这是Algol中 for
循环块的示例:
begin real a0, a1, a2, a3, z, p; integer n, i; read(a0, a1, a2, a3); read(n) for i := 1 step 1 until n do begin read(z); p := ((a3 × z + a2) × z + a1) × z + a0 print(p) end iend
现在让我们看看Algol中的TPK程序,看看与Fortran版本相比,块结构对该程序有什么帮助:
TPK begin integer i, real y; real a[0:10]; real procedure f(t); real t; value t; f := sqrt(abs(t)) + 5 × t ↑ 3; for i := 0 step 1 until 10 do read(a[i]); for i := 10 step -1 until 0 do begin y := f(a[i]); if y > 400 then write(I, 'TOO LARGE') else write(i, y); endend TPK
你应该能看出来,Algol版本的区块结构让它更像你我今天习惯阅读的代码。
转到BCPL
块结构语言的语法的下一个变化是1967年由剑桥大学的马丁·理查兹(Martin Richards)开发的BCPL语言。在1960年至1967年Algol最初开发期间,编译器和系统开发人员一直在寻找使用机器语言和汇编语言以外的语言开发系统应用程序(如操作系统)的方法。我之所以强调BCPL,是因为C是通过Ken Thompson开发的一种名为B的中间语言,作为BCPL的完善和改进而开发的。Richards将BCPL开发为一种系统开发语言,它与汇编语言一样高效,但具有更高级别的语法,从而使编码更简单、更高效。这意味着高级语言(如Algol)的许多特性需要以更有效的方式包含在BCPL等语言中。实现这一效率的方法之一是将一个代码块的指定从单词(begin
和 end
)简化为符号。对于复合语句和代码块,Richards选择了符号 $(
代表开头和 )$
代表结尾,这些符号称为节括号。在BCPL中,如果 $(
和 )$
与一个结构体一起使用,比如 if
语句或循环,那么它们就是在限定一个复合语句,如果 $(
包括一些声明,那么它们就是在给一个块划定界限。例如,下面是BCPL中带有复合语句的 if
语句的写法:
IF A < B $( LET T = A A := B; B := T $)
这是BCPL块的示例:
GET "LIBHDR"LET START() BE $( LET A, B, C, SUM = 1, 2, 3, 0 SUM := A + B + C WRITES("Sum is ") WRITEN(SUM) $)
在这里,开始部分方括号是开始一个块,因为声明紧跟着方括号。(和) 在C中成为 { 和 }大约在1968年或1969年,贝尔实验室的肯-汤普森和丹尼斯-里奇开始尝试使用系统编程语言开发操作系统。汤普森其实一开始就尝试使用Fortran进行探索,但当他意识到这不可能时,很快就放弃了。他决定要对BCPL进行修改,使其成为一种更符合自己喜好的系统编程语言,于是开发了B。B虽然更接近汤普森和里奇想要的系统开发语言,但仍然不能完全满足他们的需求,所以里奇开始为新的B开发另一种语言NB。NB的生命力并不长,最终被一种全新的语言所取代,他们自然而然地称之为C。如果你对C语言是如何从BCPL、B和NB发展而来的感兴趣,我鼓励你阅读丹尼斯-里奇的《C语言的历史》。
+=
为例)以及增量(++
)和减量(-
)运算符,作为提高语言效率的方法。此举是为了提高效率,导致BCPL的其他运算符被简化——例如,$(
和 $)
被 {
和 }
取代。我们今天的情况
在许多编程语言中,花括号已经被用作块的主要符号,特别是那些最接近C风格的语言,包括c++、Java、c#和JavaScript。更有趣的是,新的语言已经采用了大括号的使用,包括Go和Rust。事实上,Go要求对每个条件或循环构造都使用花括号,这是遵循编程专家的观点,即即使语言不需要花括号,程序员也应该对每个可以使用花括号的构造使用花括号。感谢您阅读本文,在CSS预编译器语言中,Sass使用缩进风格,Less使用花括号风格,你喜欢Sass还是Less的风格呢?请留言发表你的看法,我将随机抽取一位发送奖励红包。
精彩评论