在計算機科學中，引用透明性和引用不透明性是部分計算機程式具有的屬性。若一個表示式在被其相應的值替換時能不改變程式的行為（反之亦然），則該表示式是引用透明的。 因此，該表示式必須是純函式 給予相同的輸入，其返回值必須相同；對其求值時不能含有副作用。反之，一個不具有應用透明性的表示式是引用不透明的。 在數學中，根據構成數學函式的定義，所有函式應用都是引用透明的。然而，在程式設計中情況並非總是如此；為避免歧義，函式經常被歸類為過程或方法。函數語言程式設計的一個定義特徵是它只允許引用透明的函式。一些其他程式語言有提供方法用以選擇地保證引用透明性。一些函數語言程式設計語言要求所有函式必須引用透明。 引用透明性的重要性在於允許程式設計師和編譯器將程式行為作為重寫邏輯進行推理。這有助於證明正確性、簡化演算法、在不破壞程式碼的情況下協助修改程式碼，以及透過記憶、公共子表示式消除、惰性求值或平行計算來最佳化程式碼。 歷史 應用透明性的概念據稱起源於阿爾弗雷德諾斯懷特海和伯特蘭羅素的《數學原理》（19101913 年）。 Here p665 According to Quine the term originates from there 它被威拉德範奧曼蒯因引入分析哲學。在《詞與物》（1960 年）第 30 章中，蒯因給出瞭如下定義：如果一個包含模式 φ 是引用透明的，那麼當特指項 t 在一個術語或句子 ψt 中是純粹引用的時候，其在包含術語或句子 φψt 中也是純粹引用的。該術語出現在其當代計算機科學用法中，在程式語言中對變數的討論中，在 Christopher Strachey 的開創性講義集《程式語言中的基本概念》（1967 年）中。講義引用了蒯因的《詞與物》。 例子和反例 如果表示式中涉及的所有函式都是純函式，則該表示式是引用透明的。 假設有函式可以從某個來源接受輸入並將其返回。在虛擬碼中，該函式可能以 GetInput Source 呼叫，其中 Source 為特定的磁碟檔案、鍵盤等的標識。在多次應用 GetInput 的情況下，即使 Source 一致，連續的返回值也會不同。因此，函式 GetInput 既不是確定性的，也不是引用透明的。 一個更為微妙的例子是使函式可以具有自由變數，即某些未被明確作為引數傳遞的輸入；然後根據名稱繫結規則將其解析為非區域性變數，例如全域性變數、當前執行環境中的變數（用於動態繫結）或閉包中的變數（用於靜態繫結）。由於可以在不更改作為引數傳遞的原值的情況下更改此變數，因此即便引數相同，後續呼叫該函式的結果也可能不同。然而，在純函數語言程式設計中，破壞性賦值是不允許的，因此如果自由變數被靜態繫結到一個值，該函式仍是引用透明的，因為非區域性變數及其值都分別由於靜態繫結和不可變性而不可改變。 算術運算是引用透明的：5 5 可以替換為 25。事實上，數學意義上的所有函式都是引用透明的：sin x 是透明的，因為它總是對每個特定的 x 給出相同的結果。 重新賦值是不透明的。例如，C 語言表示式 x x 1 更改了賦給變數 x 的值。在 x 最初為 10 的情況下，對該表示式連續求值兩次會分別得出 11 和 12 。顯然，將 x x 1 替換為 11 或 12 時程式會具有不同含義，因此該表示式並不引用透明。但是，函式如是透明的，因為輸入 x 不會被隱式變更 ，因此不含有如此副作用。 today 不是透明的，當對它求值並以結果（比如， Jan 1 2001 ）進行替換時，你不會得到與明天執行它得到的相同的結果。這是因為它取決於狀態（日期）。 在無副作用的語言，例如 Haskell 中，可以 用等於代替等於：若 x y，則有 f x f y。這反映了不可分者同一性原理。對於副作用語言，該屬性一般不需要成立。即便如此，重要的是將此類斷言限制為所謂的判斷相等性，即系統測試的術語相等性，不包括使用者定義的型別等價性。例如，如果 B f A x 和型別 A 已經覆蓋了相等性的概念，例如使所有項相等，那麼在 x y 時仍有 f x f y 。這是因為像 Haskell 這樣的系統不會驗證在具有使用者定義的等價關係的型別上定義的函式是否在該等價方面得到了良好的定義。因此，引用透明性僅限於沒有等價關係的型別。將引用透明性擴充套件到使用者定義的等價關係可以透過例如 MartinLöf 身份型別來完成，但需要依賴型別的系統，例如 Agda、Coq 或 Idris。 與指令式程式設計對比 如果用其值替換表示式僅在程式執行的某個時刻有效，則該表示式不是引用透明的。這些序列點的定義和排序是指令式程式設計的理論基礎，也是指令式程式設計語言語義的一部分。 然而，因為引用透明的表示式可以在任何時候求值，所以不需要定義序列點，也不需要任何求值順序的保證。不考慮這些因素而完成的程式設計稱為純函數語言程式設計。 以引用透明的方式編寫程式碼的一個優點是靜態程式碼分析對一個智慧編譯器會更容易，並且可以自動進行更好的程式碼改進轉換。例如，在使用 C 語言程式設計時，如果在迴圈內包含對昂貴函式的呼叫，則會導致效能下降，即使可以在不更改程式結果的情況下將函式呼叫移出迴圈。程式設計師此時將被迫執行呼叫的手動程式碼移動，但這可能會以犧牲原始碼的可讀性為代價。但是，如果編譯器能夠確定函式呼叫是引用透明的，它可以自動執行此轉換。 強制引用透明性的語言的主要缺點是它們使自然適合步驟序列指令式程式設計風格的操作的表達更加笨拙和不夠簡潔。這些語言通常包含使這些任務更容易的機制，同時保留語言的純功能質量，例如定字句語法和單子。 又例 有兩個函式，一個是引用透明的（rt），另一個是引用不透明的（ro）： int g 0 int rt int x return x 1 int ro int x g return x g 函式 rt 是引用透明的，這意味著若 x y 則有 rt x rt y 。例如， rt 6 7 。但是不能確定 ro 同樣引用透明，因為它使用了由它自己修改的全域性變數。 ro 的引用不透明性使得對程式的推理更加困難。假設希望對以下陳述進行推理： int i ro x ro y ro x ro x 人們可能會想將這一陳述簡化為： int i ro x ro y 0 int i ro x 0 int i ro x 然而這並不正確，因為 ro x 每次都會計算出不同的值。需要注意的是， ro 的返回值基於未傳入的全域性值 g，並且每次呼叫 ro 時 g 都會被修改。這意味著數學恆等式在此不有效，而是適用於像 rt 這樣引用透明的函式。 但是，可以使用更復雜的分析來將語句簡化為： int tmp g int i x tmp 1 y tmp 2 x tmp 3 x tmp 4 g g 4 int tmp g int i x tmp 1 y tmp 2 x tmp 3 x tmp 4 g g 4 int tmp g int i x tmp 1 y tmp 2 1 g g 4 int tmp g int i x tmp 1 y tmp 2 g g 4 int i x y 1 g g 4 這需要更多步驟，並且要求程式設計師對編譯器最佳化不可行的程式碼有一定程度的洞察力。 因此，引用透明性使程式設計者能夠推理其程式碼，這將導致更健壯的程式，找到不希望透過測試找到的錯誤的可能性，以及看到最佳化機會的可能性。 另見 計算機科學中的冪等性 里氏替換原則 重寫規則 參考 外部連結 httpuserpagefuberlinderampubpub_jf47ht81Htreferential_transparency httpsstackoverflowcoma9859966655289 作者： Prof 烏代雷迪 （伯明翰大學） httpokmijorgftpComputationPrincipiaMathematicatxt Category 程式設計語言理論