指定叙述

在程序设计中，指定叙述（assignment statement），会将一个特定的值设定到某个特定的储存位址去，这个位置被标记成一个特定的变数名称。换句话说，这个叙述会复制一个值到某个特定变数中。在多数的指令式程式语言中，这种叙述是其中最基础的结构。

指定叙述的通用表示方法通常是 `x = expr`（这种表示法最早源自1949年–1951年时的Superplan，因为1957年首版的Fortran与C语言而广为人知），另一种形式则为 `x := expr`（这种形式最早来自ALGOL 58，因为Pascal而盛行）。在这两种表示法之外，仍然存在许多其他的形式。

对多数的指令式程式语言来说，指定叙述允许某个特定变数，在其生命周期与作用域之中，可以被指定为不同的值，或是重复被指定值。

## 语义

在指令式编程中，随着时间改变，不同的值被关联到某个特定的变数名称上。变数是数值的容器。可以先指派变数为某个值，在之后再用另一个值来加以取代。在这种模型中，程式的运作，是透过每次成功的指定叙述，来改变其状态。指令式程式语言，倚靠指定叙述来进行迭代。在最低的层级中，指定叙述是以组合语言指令，如 `MOVE` 或 `STORE`来实作。

以C语言为例，下列的程式码段落可以作为指定叙述的例子：

```c
int x = 10; 
float y;
x = 23;
y = 32.4f;
```

在第一行程式码中，变数`x`先被宣告为int型别，之后将数值10指定给它。在第二行，变数`y`被宣告为float型别，但没有指定值。在第三行，变数`x`被重新指定为数值23。在第四行中，变数`y`被指定为浮点数值32.4f。

## 单赋值

任何改变现存值的赋值（比如`x := x + 1`），在纯函数式语言中都是不允许的。在现今的函数式编程中，赋值是被劝阻的，用以支持也叫做“初始化”的单赋值。单赋值是名字绑定的用例，不同于本文其他部分描述的赋值之处在于，它只能做一次，通常是在变量被创建的时候，不允许后续的重新赋值。

表达式的求值，如果不改变机器的可察见状态，并且对相同的输入产生相同的值，就没有副作用。指令式赋值，在销毁旧值并使之不可获得时，在将旧值替代为新值时，就可能介入了副作用；为此在LISP和函数式编程中，这被称为“破坏性”（destructive）赋值，类似于“破坏性更新”。

在纯函数式语言比如Haskell中，单赋值是赋值的唯一形式，这里没有在指令式语言意义上的变量，而是命名的常量值，并具有可能的合成（compoud）本性，即它们的元素"在需要时"被逐步的定义。纯函数式编程和与之于此共通的数据流程编程，由于值之间相互独立，可以提供在并行计算上的优势，它避免了顺序的一时一步执行的冯·诺伊曼瓶颈。

非纯函数式语言，同时提供了单赋值和真赋值（尽管相比指令式编程语言而言真赋值典型的较少使用）。例如，在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`。

### 链式赋值

语句如`w = x = y = z`叫做“链式赋值”，其中`z`的被赋给多个变量`w`、`x`和`y`。链式赋值经常用来初始化多个变量，比如`a = b = c = d = f = 0`。

### 并行赋值

一些编程语言，比如APL、Common Lisp、Go、JavaScript（自从1.7）、Lua、Maple、occam 2、Perl、PHP、Python、REBOL、Ruby、Windows PowerShell，允许多个变量被并行的赋值，语法如下：

```
a, b := 0, 1
```

它同时赋值`0`到`a`和`1`到`b`。这经常叫做并行（parallel）赋值；它是CPL语言于1963年介入的，当时名字叫做同时（simultaneous）赋值，有时也叫做多（multiple）赋值，但这在与单（single）赋值一起用时会产生混淆，因为它们不是对比的。如果赋值的右手侧是一个单一变量（比如一个数组或结构），这个特征就叫做解包（unpacking）或解构（destructuring）赋值：

```
'''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的例子：

```python
def f():
    return 1, 2
a, b = f()
```

而其他语言，比如C#，要求使用圆括号的显式元组构造和解构，如下面例子这样：

```csharp
(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中，被替代为并行赋值。但是上述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 (和派生者比如Modula), Mary (编程语言), PL/M, Ada, Smalltalk, Eiffel, Oberon, Dylan, Seed7, Go (声明和定义变量的快捷方式), Io, AMPL, ML, AutoHotkey等。

其他可能性包括左箭头或关键字，但还有其他更罕见的变体：

`''variable'' << ''expression''`｜Magik (编程语言)

`''variable'' <- ''expression''`｜F#, OCaml, R, S

`''variable'' <<- ''expression''`｜R

`assign("''variable''", ''expression'')`｜R

`''variable'' ← ''expression''`｜APL, 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

`set ''variable'' ''expression''`｜Tcl

`''expression'' ''variable'' !`｜Forth