热门问题
时间线
聊天
视角
块 (编程)
来自维基百科,自由的百科全书
Remove ads
在電腦編程中,塊(block)或譯為程式區塊、代碼塊,是將原始碼組織在一起的詞法結構。塊構成自一個或多個聲明和陳述式。程式語言允許建立塊,包括嵌入其他塊之內的塊,就叫做塊結構程式語言。塊和子程式是結構化編程的基礎,結構化所強調的控制結構可以用塊來形成的。
在編程中塊的功能,是確使成組的陳述式被當作如同就是一個陳述式,限定在一個塊中聲明的對象如變量、過程和函數的詞法作用域,使得它們不衝突於在其他地方用到的同名者。在塊結構程式語言中,在塊外部的對象名字在塊內部是可見的,除非它們被聲明了相同名字的對象所遮掩。
歷史
塊結構的想法是在1950年代開發最初的Autocode期間發展出來的,並形式化於ALGOL 60報告中。ALGOL 58介入了「複合」(compound)陳述式的概念,它只與控制流程有關[1]。在「ALGOL 60報告」中,介入了塊和作用域的概念[2]。最終在「修訂報告」中,複合陳述式被定義為:包圍在陳述式括號begin
和end
之間的成序列的陳述式,形成一個複合陳述式。塊被定義為:成序列的聲明,跟隨着成序列的陳述式,並被包圍在begin
和end
之間,形成一個塊;所有聲明以這種方式出現在一個塊中,並只在這個塊中有效[3]。塊與複合陳述式的主要差異是不能從塊外跳轉到塊內的標籤[4]。
語法
塊在不同語言家族中使用不同的語法:
- ALGOL語言家族,ALGOL 60及其後繼者比如Simula,使用陳述式括號
begin
和end
來界定複合陳述式和塊。 - Lisp語言家族,Lisp 1.5使用具有語法關鍵字
prog
的S-表達式表示塊[9],而Maclisp和Scheme使用let
形式的S-表達式來表示塊[10],S-表達式是圓括號(
和)
包圍的字首表示法。 - Smalltalk語言家族,Smalltalk-80和Self使用方括號
[
和]
來界定塊。
此外,複合陳述式界定還可以採用:
建立控制結構,除了將所控制的陳述式序列,包圍入複合陳述式或匿名塊之外,還可以採用其他語法機制:
- 在ALGOL 68中,條件和迭代陳述式,使用塊首保留字的反寫保留字來終止,比如:
IF ~ THEN ~ ELIF ~ THEN ~ ELSE ~ FI
和FOR ~ FROM ~ TO ~ BY ~ WHILE ~ DO ~ OD
。繼承此風格的有:Dijkstra的守衛命令語言和Bourne的Bourne shell等。 - 一些結構化編程語言,如FORTRAN 77、Modula-2、Ada和Visual Basic等,對控制結構加結束關鍵字,比如Modula-2中的:
IF ~ THEN ~ ELSIF ~ THEN ~ ELSE ~ END
和FOR ~ TO ~ BY ~ DO ~ END
。
Remove ads
限制
受ALGOL影響的一些語言支援塊,但有着各自的限制:
基本語意
塊的語意是雙重的。首先,它向編程者提供了建立任意大和複雜的結構,並把它當作一個單元的一種途徑。其次,它確使編程者能限制變量的作用域,有時可以限制已經被聲明了的其他對象的作用域。
在早期語言比如FORTRAN和BASIC中,沒有陳述式塊或控制結構。直到1978年標準化FORTRAN 77之前,都沒有「塊狀IF
」陳述式,要實現按條件選擇,必須訴諸GOTO
陳述式。例如下述FORTRAN代碼片段,從僱員工資中分別扣除超出正稅閾值部分的稅款,和超出附加稅閾值部分的附加稅款:
C 语言:ANSI标准FORTRAN 66
C 初始化要计算的值
PAYSTX = .FALSE.
PAYSST = .FALSE.
TAX = 0.0
SUPTAX = 0.0
C 如果雇员挣钱小于等于正税阈值则跃过税款扣除
IF (WAGES .LE. TAXTHR) GOTO 10
PAYSTX = .TRUE.
TAX = (WAGES - TAXTHR) * BASCRT
10 CONTINUE
C 如果雇员挣钱小于等于附加税阈值则跃过附加税扣除
IF (WAGES .LE. SUPTHR) GOTO 20
PAYSST = .TRUE.
SUPTAX = (WAGES - SUPTHR) * SUPRAT
20 CONTINUE
TAXED = WAGES - TAX - SUPTAX
程式的邏輯結構不反映在代碼中,這裏的初始化的值,是後面的有關邏輯判斷為假時所應當設置的值。
塊允許編程者把一組陳述式當作一個單元。例如,在與上述FORTRAN代碼相對應的Pascal代碼片段:
{ 语言:Jensen与Wirth版标准Pascal }
if Wages > TaxThreshold then
begin
PaysTax := true;
Tax := (Wages - TaxThreshold) * TaxRate
end
else begin
PaysTax := false;
Tax := 0
end;
if Wages > SupertaxThreshold then
begin
PaysSupertax := true;
Supertax := (Wages - SupertaxThreshold) * SupertaxRate
end
else begin
PaysSupertax := false;
Supertax := 0
end;
Taxed := Wages - Tax - Supertax;
與上述FORTRAN代碼相比,上例中出現在初始化中的那些預設值,通過複合陳述式即不帶聲明的塊結構,被分別放置作出有關邏輯判斷的地方。使用塊結構,能明晰編程者的意圖,使代碼的結構更加密切反映出編程者的思考;再憑藉某種風格的縮排和駝峰式大小寫增進可讀性,可使代碼更加容易理解和修改。
在早期語言中,在次常式中變量的作用域遍及整個次常式。假想在一個Fortran次常式中,完成了與管理者有關的任務,這裏可能用到叫做IEMPNO
的一個整數變量,指示作為管理者的僱員的社會安全號碼(SSN);後來在這個次常式的維護工作中,又增加與下屬們有關的任務,此時編程者可能不經意間使用同名變量IEMPNO
,指示了作為這個管理者的下屬的僱員的SSN,這就會導致一個難於跟蹤的缺陷。
塊結構使得編程者能夠容易地將作用域控制到細微級別。例如完成有關僱員任務的Scheme代碼片段:
;; 语言:R5RS标准Scheme
(let ((empno (ssn-of employee-name)))
(when (is-manager? empno) ;; when已列入R7RS-small标准
(let ((employee-list (underlings-of empno)))
(display
;; format是SRFI-28和SRFI-48规定的字符串格式化过程
(format "~a has ~a employees working under him:~%"
employee-name (length employee-list)))
(for-each
(lambda (empno)
(display
(format "Name: ~a, role: ~a~%"
(name-of empno) (role-of empno))))
employee-list))))
這裏在外層通過繫結宏let
將管理者的SSN繫結到了局部變量empno
,在其形成的塊的作用域中列出管理者的僱員名字和他的下屬數目;隨後通過for-each
高階函數,將他所有下屬的SSN逐個繫結到匿名函數lambda
的形式參數empno
上,執行此匿名函數列出這個下屬的名字和角色;這個形式參數的作用域是此匿名函數的主體,它與其外層的局部變量,識別碼重名但不相互影響。在實踐中,出於清晰性的考慮,編程者更可能選取明顯不同的變量名字,但是即使名字選取存在重複,也難以在不經意間介入一個缺陷。在基於S-表達式的語言中,經常見到大量的巢狀圓括號,故而其代碼必須採用良好的縮排。
Remove ads
提升
在一些語言中,變量可以聲明為有函數作用域即使它位於函數的內嵌塊之中。例如在JavaScript中,變量應當總是在使用之前被聲明,它曾經允許賦值到未聲明變量,會為此建立為未聲明的全域變量,這在strict
模態下是個錯誤。以var
聲明的變量有函數作用域,而非以let
或const
聲明的變量可從屬的塊作用域。以var
聲明的變量會被提升(hoist),這意味着可以在這個函數的作用域內任何地方提及這個變量,即使還未觸及到它的聲明,從而可以將var
聲明視為被提舉(lift)到它所在函數的頂部或全域作用域。但是如果在其聲明之前訪問了一個變量,這個變量的值總是未指定的。
參見
參照
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads