指定敘述
在程序設計中,指定敘述(英語:assignment statement),又譯指定指述,會將一個特定的值設定到某個特定的儲存位址去,這個位置被標記成一個特定的變數名稱。換句話說,這個敘述會複製一個值到某個特定變數中。在多數的指令式程式語言中,這種敘述是其中最基礎的結構。
指定敘述的通用表示方法通常是 x = expr
(這種表示法最早源自1949年–1951年時的Superplan,因為1957年首版的Fortran與C語言而廣為人知),另一種形式則為 x := expr
(這種形式最早來自ALGOL 58,因為Pascal而盛行)。在這兩種表示法之外,仍然存在許多其他的形式。
對多數的指令式程式語言來說,指定敘述允許某個特定變數,在其生命週期與作用域之中,可以被指定為不同的值,或是重複被指定值。
語義
[編輯]在指令式編程中,隨著時間改變,不同的值被關聯到某個特定的變數名稱上。變數是數值的容器。可以先指派變數為某個值,在之後再用另一個值來加以取代。在這種模型中,程式的運作,是透過每次成功的指定敘述,來改變其狀態。指令式程式語言,倚靠指定敘述來進行迭代。在最低的層級中,指定敘述是以組合語言指令,如 MOVE
或 STORE
來實作。
以C語言為例,下列的程式碼段落可以作為指定敘述的例子:
int x = 10;
float y;
x = 23;
y = 32.4f;
在第一行程式碼中,變數x
先被宣告為int型別,之後將數值10指定給它。在第二行,變數y
被宣告為float型別,但沒有指定值。在第三行,變數x
被重新指定為數值23。在第四行中,變數y
被指定為浮點數值32.4f。
單賦值
[編輯]任何改變現存值的賦值(比如x := x + 1
),在純函數式語言中都是不允許的[1]。在現今的函數式編程中,賦值是被勸阻的,用以支持也叫做「初始化」的單賦值。單賦值是名字綁定的用例,不同於本文其他部分描述的賦值之處在於,它只能做一次,通常是在變量被創建的時候,不允許後續的重新賦值。
表達式的求值,如果不改變機器的可察見狀態[2],並且對相同的輸入產生相同的值[1],就沒有副作用。指令式賦值,在銷毀舊值並使之不可獲得時,在將舊值替代為新值時,就可能介入了副作用[3];為此在LISP和函數式編程中,這被稱為「破壞性」(destructive)賦值,類似於「破壞性更新」。
在純函數式語言比如Haskell中,單賦值是賦值的唯一形式,這裡沒有在指令式語言意義上的變量[1],而是命名的常量值,並具有可能的合成(compoud)本性,即它們的元素"在需要時"被逐步的定義。純函數式編程和與之於此共通的數據流程編程,由於值之間相互獨立,可以提供在並行計算上的優勢,它避免了順序的一時一步執行的馮·諾伊曼瓶頸[4]。
非純函數式語言,同時提供了單賦值和真賦值(儘管相比指令式編程語言而言真賦值典型的較少使用)。例如,在Scheme中,單賦值(通過let
),和真賦值(通過set!
),二者都可以用於所有變量上,並提供專門的原語(primitive)用於在列表、向量、字符串等之內做破壞性更新。在OCaml中,只有單賦值,通過let name = value
語法,被允許用於變量;而破壞性更新,可通過單獨的<-
算符,用於數組的元素和字符串,還可用於已經被編程者顯式聲明為可變(意味着能夠在其初始化聲明之後被變更)的記錄字段和對象。
使用單賦值的函數式編程語言,包括Clojure(針對數據結構,而非變量)、Erlang(相比Haskell,它接受多次賦值,如果值相等的話)、F#、Haskell、Lava、OCaml、Oz(對用數據流變量,而非cell)、Racket(對於一些數據結構如列表,而非符號)、SASL、Scala(對於變量)、SISAL、Standard ML。非回溯的Prolog代碼可以被看作「明顯的」單賦值,這裡明顯的含義為,它的(命名)變量可以顯式的處在未賦值狀態,或只能準確的被設置一次。相反的,在Haskell中,沒有未賦值變量,而所有變量可以看作在創建時就被隱式的設定了它的值(更精確的說是設置了計算對象在「在需要時」產生它的值)。
指定語句的回傳值
[編輯]在一些編程語言中,指定敘述的整個語句可能會傳回某種型別的一個值,而在其它語言中則不會。
在 C 編程語言中指定語句只會單純返回指定值,而允許這樣子的片語 x = y = a
,其中指定語句 y = a
返回值 a
,然後將值指定到 x
。在諸如 while ((ch = getchar()) != EOF) {…}
的語句中,函數的返回值可用於控制迴圈,同時將相同的值指定給變量 ch
。
在其它編程語言中例如 Scheme,指定語句的返回值是未定義的,而且這些片語無效。
在 Haskell 中沒有變量指定;但類似於指定的操作(如分配給數組的字段或可變數據結構的字段)通常以 unit
型別為單位進行求值,unit
型別以 ()
表示。這種型別只有一個可能的值,因此不包含任何信息。它通常是純粹為了副作用而評估的表達型別。
賦值的變體形式
[編輯]特定使用模式也非常常見,因此經常有支持它們的特殊語法。這些主要是減少源代碼冗長的語法糖,但也能輔助代碼讀者理解編程者的意圖,並提供給編譯器進行可能的優化的線索。
增廣賦值
[編輯]所賦予的值依賴於先前的值是很常見的,很多指令式語言,尤其是C及其主要派生者,提供了叫做增廣賦值的特殊算符,比如*=
,則a = 2*a
可以轉而寫為a *= 2
[5]。
鏈式賦值
[編輯]語句如w = x = y = z
叫做「鏈式賦值」,其中z
的被賦給多個變量w
、x
和y
。鏈式賦值經常用來初始化多個變量,比如a = b = c = d = f = 0
。
並行賦值
[編輯]一些編程語言,比如APL、Common Lisp[6]、Go[7]、JavaScript(自從1.7)、Lua、Maple、occam 2[8]、Perl[9]、PHP、Python[10]、REBOL、Ruby[11]、Windows PowerShell,允許多個變量被並行的賦值,語法如下:
a, b := 0, 1
它同時賦值0
到a
和1
到b
。這經常叫做並行(parallel)賦值;它是CPL語言於1963年介入的,當時名字叫做同時(simultaneous)賦值[12],有時也叫做多(multiple)賦值,但這在與單(single)賦值一起用時會產生混淆,因為它們不是對比的。如果賦值的右手側是一個單一變量(比如一個數組或結構),這個特徵就叫做解包(unpacking)[13]或解構(destructuring)賦值[14]:
var list := {0, 1} a, b := list
這個列表將被解包使得賦值0
至a
和1
至b
。進一步的:
a, b := b, a
對換a
和b
的值。在沒有並行賦值的語言中,這必須通過臨時變量來書寫:
var t := a a := b b := t
因為a := b; b := a
將把a
和b
二者都賦值為b
最初的值。
一些語言,比如Go和Python,將並行賦值、元組和自動元組解包結合起來,允許從一個單一函數返回多個值,比如如下Python的例子:
def f():
return 1, 2
a, b = f()
而其他語言,比如C#,要求使用圓括號的顯式元組構造和解構,如下面例子這樣:
(a, b) = (b, a);
(string, int) f() => ("foo", 1);
var (a, b) = f();
這提供了從一個函數返回多個值要使用輸出參數的一種替代方式。這最早見於CLU語言(1974年),而CLU推動了一般的並行賦值變得流行。
在C和C++中,逗號運算符,在允許多個賦值出現在一個單一語句上類似於並行賦值,寫a = 1, b = 2
替代a, b = 1, 2
。這主要用在for循環中,在其他語言比如Go中,被替代為並行賦值[15]。但是上述C++代碼不確保完全的同時性,因為代碼a = b, b = a+1
的右側項是在左側項之後運算的。在語言如Python中,a, b = b, a+1
將並發的賦值兩個變量,使用最初的a
的值來計算新b
的值.
指定與等式符號
[編輯]標記法
[編輯]複製分配的兩個最常見的表示形式是等號(=
)和冒號等於(:=
)。這兩種形式都可以在語義上表示賦值語句或賦值運算符(它也具有值),這取決於語言用法。
variable = expression
Fortran, PL/I, C (和派生者比如C++, Java等), Bourne shell, Python, Go (賦值預先聲明的變量), R, Windows PowerShell等。 variable := expression
ALGOL (和派生者), Simula, CPL, BCPL, Pascal[16] (和派生者比如Modula), Mary, PL/M, Ada, Smalltalk, Eiffel[17][18], Oberon, Dylan[19], Seed7, Go (聲明和定義變量的快捷方式)[20], Io, AMPL, ML[21], AutoHotkey等。
其他可能性包括左箭頭或關鍵字,但還有其他更罕見的變體:
variable << expression
Magik variable <- expression
F#, OCaml, R, S variable <<- expression
R assign("variable", expression)
R variable ← expression
APL[22], Smalltalk variable =: expression
J LET variable = expression
BASIC let variable := expression
XQuery set variable to expression
AppleScript set variable = expression
C shell Set-Variable variable (expression)
Windows PowerShell variable : expression
Macsyma, Maxima, Rebol var variable expression
mIRC腳本語言 reference-variable :- reference-expression
Simula
數學偽代碼分配通常用左箭頭表示。有些平台將表達式放在左側,變量放在右側:
MOVE expression TO variable
COBOL expression → variable
TI-BASIC, Casio BASIC expression -> variable
BETA, R put expression into variable
LiveCode
一些面向運算式的語言比如 Lisp 和 Tcl,對所有語句(包括賦值)統一使用前綴(或後綴)語法。
(setf variable expression)
Common Lisp (set! variable expression)
Scheme[23][24][25] set variable expression
Tcl expression variable !
Forth
另見
[編輯]註釋
[編輯]- ^ 1.0 1.1 1.2 Crossing borders: Explore functional programming with Haskell 網際網路檔案館的存檔,存檔日期November 19, 2010,., by Bruce Tate
- ^ Mitchell, John C. Concepts in programming languages. Cambridge University Press. 2003: 23 [3 January 2011]. ISBN 978-0-521-78098-8.
- ^ Imperative Programming Languages (IPL) (PDF). gwu.edu. [20 April 2018]. (原始內容存檔 (PDF)於2021-01-18).
- ^ John C. Mitchell. Concepts in programming languages. Cambridge University Press. 2003: 81–82 [3 January 2011]. ISBN 978-0-521-78098-8.
- ^ Ruediger-Marcus Flaig. Bioinformatics programming in Python: a practical course for beginners. Wiley-VCH. 2008: 98–99 [25 December 2010]. ISBN 978-3-527-32094-3. (原始內容存檔於2017-04-19).
- ^ CLHS: Macro SETF, PSETF. Common Lisp Hyperspec. LispWorks. [23 April 2019]. (原始內容存檔於2020-11-30).
- ^ The Go Programming Language Specification: Assignments (頁面存檔備份,存於網際網路檔案館)
- ^ INMOS Limited (編). Occam 2 Reference Manual. New Jersey: Prentice Hall. 1988. ISBN 0-13-629312-3.
- ^ Wall, Larry; Christiansen, Tom; Schwartz, Randal C. Perl Programming Language 2. Cambridge: O´Reilly. 1996. ISBN 1-56592-149-6.
- ^ Lutz, Mark. Python Programming Language 2. Sebastopol: O´Reilly. 2001. ISBN 0-596-00085-5.
- ^ Thomas, David; Hunt, Andrew. Programming Ruby: The Pragmatic Programmer's Guide. Upper Saddle River: Addison Wesley. 2001. ISBN 0-201-71089-7.
- ^ D.W. Barron et al., "The main features of CPL", Computer Journal 6:2:140 (1963). full text (subscription) Archive.is的存檔,存檔日期2012-07-07
- ^ PEP 3132 -- Extended Iterable Unpacking. legacy.python.org. [20 April 2018]. (原始內容存檔於2016-05-13).
- ^ Destructuring assignment. MDN Web Docs. [20 April 2018]. (原始內容存檔於2021-02-10).
- ^ Effective Go (頁面存檔備份,存於網際網路檔案館): for (頁面存檔備份,存於網際網路檔案館), "Finally, Go has no comma operator and ++ and -- are statements not expressions. Thus if you want to run multiple variables in a for you should use parallel assignment (although that precludes ++ and --)."
- ^ Moore, Lawrie. Foundations of Programming with Pascal. New York: John Wiley & Sons. 1980. ISBN 0-470-26939-1.
- ^ Meyer, Bertrand. Eiffel the Language. Hemel Hempstead: Prentice Hall International(UK). 1992. ISBN 0-13-247925-7.
- ^ Wiener, Richard. An Object-Oriented Introduction to Computer Science Using Eiffel. Upper Saddle River, New Jersey: Prentice Hall. 1996. ISBN 0-13-183872-5.
- ^ Feinberg, Neal; Keene, Sonya E.; Mathews, Robert O.; Withington, P. Tucker. Dylan Programming. Massachusetts: Addison Wesley. 1997. ISBN 0-201-47976-1.
- ^ The Go Programming Language Specification - The Go Programming Language. golang.org. [20 April 2018]. (原始內容存檔於2021-03-18).
- ^ Ullman, Jeffrey D. Elements of ML Programming: ML97 Edition. Englewood Cliffs, New Jersey: Prentice Hall. 1998. ISBN 0-13-790387-1.
- ^ Iverson, Kenneth E. A Programming Language. John Wiley and Sons. 1962. ISBN 0-471-43014-5. (原始內容存檔於2009年6月4日).
- ^ Dybvig, R. Kent. The Scheme Programming Language: ANSI Scheme. New Jersey: Prentice Hall. 1996. ISBN 0-13-454646-6.
- ^ Smith, Jerry D. Introduction to Scheme. New Jersey: Prentice Hall. 1988. ISBN 0-13-496712-7.
- ^ Abelson, Harold; Sussman, Gerald Jay; Sussman, Julie. Structure and Interpretation of Computer Programs. New Jersey: McGraw-Hill. 1996. ISBN 0-07-000484-6.