赋值语句
在程序设计中,赋值语句(英语:assignment statement),会将一个特定的值设置到某个特定的存储地址去,这个位置被标记成一个特定的变量名称。换句话说,这个叙述会复制一个值到某个特定变量中。在多数的指令式编程语言中,这种叙述是其中最基础的结构。
赋值语句的通用表示方法通常是 x = expr
(这种表示法最早源自Superplan 1949–51,因为 Fortran 1957与C语言而广为人知。),另一种形式则为 x := expr
(这种形式最早来自ALGOL 1958,因为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], 等。
其他可能性包括左箭头或关键字,但还有其他更罕见的变体:
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].
- ^ 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.
- ^ CLHS: Macro SETF, PSETF. Common Lisp Hyperspec. LispWorks. [23 April 2019].
- ^ 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)
- ^ PEP 3132 -- Extended Iterable Unpacking. legacy.python.org. [20 April 2018].
- ^ Destructuring assignment. MDN Web Docs. [20 April 2018].
- ^ 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].
- ^ 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.