在程序設計 中,指定敘述 (英語: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 等。
其他可能性包括左箭頭或關鍵字,但還有其他更罕見的變體:
數學偽代碼分配通常用左箭頭表示。有些平台將表達式放在左側,變量放在右側:
一些面向運算式的語言比如 Lisp 和 Tcl,對所有語句(包括賦值)統一使用前綴(或後綴)語法。
另見
註釋
^ 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 .