指定敘述

在程序設計中，指定敘述（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