關係運算子在計算機科學的編程語言中,是測試或定義兩個實體之間某種關係的構造或操作符。一共有六种關係,分别为:小于(<)大于(>)小于或等于(<=)大于或等于(>=)等于(==)不等于(<>)。在具備布爾型別的編程語言中(如 Pascal,Ada 或 Java),這些運算符通常根據兩個操作變量之間的條件關係是否成立,判定為真(True)或假(False)。諸如 C 語言中關係運算子傳回整數 0 或 1,其中 0 表示假,任何非零值表示真。使用關係運算子創建的表達式,形成所謂的關係表達式或條件。 關係運算子可以被視為謂詞邏輯的特殊情況。

相等性

编辑

用法

编辑

許多編程語言的構造和資料型別中都使用到相等性,用於測試元素是否已存在於集合中,或者藉由鍵來存取值。它在切換(switch)語句,以及編程的邏輯併聯過程中,用於將控制流調度到正確的分支。相等性的可能含義之一是“如果 a 等於 b,那麼我們可以在任何情況下互換 a 或 b,而不會產生任何差異。”但這樣的聲明不一定成立,尤其在將可變性和內容等同性一起考慮時。

物件相等與內容等同性

编辑

有時,特別是在物件導向編程中,對資料型別和繼承物件進行比對時,出現了相等性和辨別的問題。以下情況通常需要區別:

  • 相同型別的兩個不同物件,例如兩隻手
  • 兩個物件相等但不同,例如兩張10元鈔票
  • 兩個物件相等但有不同的呈現,例如$1元紙鈔和$1元硬幣
  • 對同一物件的兩個不同參照,例如,同一人的兩個暱稱

在許多現代編程語言中會藉由參照來存取物件和資料結構。在這些語言中,需要測試兩種相等性質:

  • 實質同等性:如果有兩個參照A和B來自引用同一個物件,以A與物件進行的互動,跟藉由B與物件進行的互動,兩者其實就是相同作用而無法區別,特別是以A去改變物件的異動會反映在B之上。當討論為值而非物件時,實質同等性並不適用。
  • 語義同等性:如果兩個參照物件或兩個值在某種意義上是等價的:
    • 結構等式(即它們的內容是相同的),或淺薄地(僅測試目前部份)或深入地(遞歸地測試其所有部份的相等性)。實現這一點的簡易方法是通過代表等式:檢查參照的值是否有相同的代表式。
    • 其它特制的同等性,保留外部行為。例如將  視為有理數時,被判斷是相等的。除了反射性對稱性傳遞性之外,對 A = B 特制的定義可能是“若且唯若對於物件A和物件B之上的所有操作,都將具有相同的結果時,則 A = B ”。

第一種同等性質通常蘊涵著第二種同等性質(除了非數字類(not a number, NaN),它們不等於自身),但反向的同等性質並不一定成立。例如兩個字串物件可以是不同物件(第一種意義不相等),但它們包含相同的字元序列(第二種意義上相等)。有關此問題的更多信息,請參閱識別(identity)。

實數中包括許多簡分數,無法以浮點算數精確地表示,所以需要在給定誤差範圍內來測試相等性。但這樣的誤差範圍將打破一些例如傳遞性、反身性的要求性質:IEEE浮點標準是判斷 Nan ≠ NaN 成立(NaN不等於自身)。

其他編程元素例如可計算的函數,可能沒有相等性的意義,或者相等性是不能計算的。由於這些原因,一些語言以基礎類別、介面、特點(trait)或協定的形式,定義了“可比較”的明確概念,以源碼中的顯式聲明,被藉由型別的結構,來使用關係運算。

比較不同類型的值

编辑

JavaScript,PHP 和一些其它動態型別的語言中,如果兩個值相等,等號運算符將計算為真,即使它們實際上為不同型別的物件,例如以數值4和字串"4"相比較,結果會是相等。在這類語言中通常也會提供型別相等運算子,僅對具有相同或等價型別的物件比較返回真(在PHP 5中 4 ==="4"為假,但 4 =="4" 為真)。而在將數值0也當作布爾值為假的編程語言中,該運算子可化簡為檢查物件是否為數值零(例如,對於數值0或字串"0"的x物件,使用型別相等運算子,則 x == 0 判斷傳回真值)。

次序比較

编辑

非數值資料的次序比較(大於或小於)運算是根據排序慣例(例如字串依照編程語言內定的字典次序,和/或可由開發人員設定的)。當兩個資料項 a 和 b 之間的比較結果,要和數值關聯時,通常慣例是如果 a < b 則結果賦值為 -1,如果 a = b 則為 0,如果 a > b 則為 1。例如C語言的函數strcmp執行三方向比較,並根據此慣例返回 -1, 0 或 1,而qsort預期比較函數依此慣例返回值。在排序演算法中比較方法源碼的效率至為關鍵,因為它是排序性能的主要因素之一。

開發人員定義的資料型別(不是編程語言內建的型別)的比較,可以編寫自訂的或使用函式庫的函數(如上文的strcmp)來執行,或者在某些語言中通過重載比較運算符-即以開發人員的定義指派給比較運算子,來比較特定資料型別。另一個選擇是使用某些慣例,例如成員比較。

邏輯等價

编辑

雖然一開始可能不那麼顯而易見,像布爾邏輯運算符 XOR,AND,OR 和 NOT,這些關係運算子可以設計為具有邏輯等同性,使得它們都可以相互定義。對於任何給定的 x 和 y 值,以下四個條件語句都有相同的邏輯等價性 E(全為真或全為假):

 

這依賴於域是良好排序的。

標準關係運算符

编辑

在編程語言中最常見到的數值關係運算子如下所示。

Common relational operators
Convention equal to not equal to greater than less than greater than
or equal to
less than
or equal to
In print = > <
FORTRAN[note 1] .EQ. .NE. .GT. .LT. .GE. .LE.
ALGOL 68[note 2] = > <
/= >= <=
eq ne gt lt ge le
APL = > <
BASIC-like, spreadsheet formulas[note 3] = <> > < >= <=
MUMPS = '= > < '< '>
Lua == ~= > < >= <=
Pascal-like[note 4] = <> > < >= <=
C-like[note 5] == != > < >= <=
Bourne-like shells[note 6] -eq -ne -gt -lt -ge -le
Batch file EQU NEQ GTR LSS GEQ LEQ
MATLAB[note 7] == ~= > < >= <=
eq(x,y) ne(x,y) gt(x,y) lt(x,y) ge(x,y) le(x,y)
Fortran 90[note 8] == /= > < >= <=
Mathematica[1] == != > < >= <=
Equal[x,y] Unequal[x,y] Greater[x,y] Less[x,y] GreaterEqual[x,y] LessEqual[x,y]
  1. ^ Including FORTRAN II, III, IV, 66 and 77.
  2. ^ ALGOL 68: stropping regimes are used in code on platforms with limited character sets (e.g., use >= or GE instead of ), platforms with no bold emphasis (use 'ge'), or platforms with only UPPERCASE (use .GE or 'GE').
  3. ^ Including Visual Basic .NET, OCaml, SQL, Standard ML, Excel, and others.
  4. ^ Including ALGOL, Simula, Modula-2, Object Pascal (Delphi), OCaml, Standard ML, Eiffel, APL, and others.
  5. ^ Including C, C++, C#, Go, Java, JavaScript, Perl (numerical comparison only), PHP, Python, Ruby, and R.
  6. ^ Including Bourne shell, Bash, Korn shell, and Windows PowerShell. The symbols < and > are usually used in a shell for redirection, so other symbols must be used. Without the hyphen, is used in Perl for string comparison.
  7. ^ MATLAB, although in other respects using similar syntax as C, does not use !=, as ! in MATLAB sends the following text as a command line to the operating system. The first form is also used in Smalltalk, with the exception of equality, which is =.
  8. ^ Including FORTRAN 95, 2003, 2008 and 2015.

其他較少見的:Common Lisp的不等關係運算子是 /=,Macsyma/Maxima 的不等關係運算子是 #。舊的Lisp使用equal,greaterp 和 lessp; 而以not運算子作邏輯否定。

語法

编辑

關係運算子也用於技術文獻而不是單詞,如果編程語言支援通常以中綴表示法,亦即出現在其操作變量(兩個表達式是相關的)之間。 舉例而言如果 x 小於 y,在Python中的表達式將印出句子:

if x < y:
    print("x is less than y in this example")

其他編程語言如 Lisp 使用前綴表示法,如下所示:

(>= X Y)

操作符鏈接

编辑

鏈接關係在數學中是普遍的寫法,例如 3 < x < y < 20 表示 3 < x 而且 x < y 而且 y <20。語義是很清楚的,因為數學中這些關係運算是有傳遞性的。然而,許多最近的編程語言會把 3 < x < y 的表達式,看作兩個左(或右)關係運算子的組合,而解譯為(3 < x ) < y。如果我們設 x = 4 則得到(3 < 4 )< y,而運算式變成true < y,這是無意義的。但它卻可能通過 C/C++ 和一些其它語言的編譯(因為 true 會以數值1代表)。

有些編程語言如Python和Perl 6 能正確給出x < y < z表達式所代表的數學意義,其它種語言則不, 部份是因大多數運算符在C語言種類中,以中綴表示法的運作方式有所不同。D編程語言保持與C的一些兼容性,而“允許C語言表達式卻有微妙不同的語義(雖然可說是方向正確),與便利性比起來造成更多的混淆”。

有些語言如 Common Lisp,對此則使用多參數謂詞。當 x 在 1 和 10 之間時,評估比較運算式 (<= 1 x 10)結果為真。

與賦值運算子的混淆情況

编辑

早期(西元1956-57年)FORTRAN編程語言受限於有限的字集,其中等號“=”是唯一的關係運算子,沒有數學上通用的大於“<”或小於“>”關係符號(當然也就沒有不大於“≤”或不小於“≥”之類的關係符號),迫使設計者定義如.GT..LT..GE..EQ.這樣的關係符號,隨後等號“=”字符被人借用來執行複製,儘管此用法與數學意義明顯不一致(X = X + 1 在數理是不能成立的)。

因此國際代數語言(IAL,ALGOL 58)和 ALGOL(1958和1960)引入了“:=”表示賦值操作,留下等號“=”字符作為相等關係的標準,遵循這個慣例的編程語言有CPL,ALGOL W,ALGOL 68,BCPL,Simula,SET(SETL),Pascal,Smalltalk,Modula-2,Ada,Standard ML,OCaml,Eiffel,Object Pascal(Delphi),Oberon,Dylan,VHSIC(VHDL)等。

B 和 C 編程語言

编辑

大多數編程語言遵循的這種事實標準,後來被名為B的極簡編譯語言間接改變。它唯一的應用目標是作為(一個非常原始的)Unix的最初移植版本,但它也演變成非常有影響力的 C 編程語言。

B 最初是系統編程BCPL的語法變體,簡化(無型別)的CPL版本。在描述為 “拆解” 過程的情況下,BCPL的交集和聯集運算子被替換為&|(後來變成&&||)。

同樣的過程中,原來具有ALGOL風格在BCPL語言中表示賦值操作的:=符號,在B語言中被替換為=。導致這種演變過程的原因未知。由於變量賦值在B語言中沒有特殊語法(例如 let 或類似),而在表達式中允許這個操作,所以等號的傳統語義(相等關係)和非標準涵義(變量賦值)另外相關聯在一起。為了區分這兩種意義,因此Ken Thompson使用了特別的雙等號==組合取代相等關係判斷。

一個小的型別系統後來被引入,B接著演變成C。C語言的普及與Unix的關聯,使Java,C#和許多其他語言沿用這種語法,雖然已經大不相同於等號的數學關係涵義。

編程語言

编辑

C編程的賦值語句會有返回值,由於任何非零值在條件運算式中被解譯為真,源碼if(x = y)是合法的,但與if(x == y)的意義完全相異。前者語義為“將 y 賦值給 x,如果 x 的新值不為 0,則執行以下語句”;後者語義則為“如果僅當 x 等於 y,執行以下語句”。

  int x = 1;
  int y = 2;
  if (x = y) {
      /* This code will always execute if y is anything but 0*/
      printf("x is %d and y is %d\n", x, y);
  }

雖然Java和C#具有與C相同的運算子,但這種錯誤通常會導致這些編程的編譯錯誤,因為條件式必須是布林型別,而且沒有隱式方法能從其它類型(如數值)轉為布林型別。 因此,除非被賦值的變量具有布林型別(或包裝為布林型別),否則會產生編譯錯誤。

ALGOL類的語言中例如Pascal,Delphi和Ada(允許其編程可定義嵌套函數),Python和許多函數語言中,賦值運算子不可出現在表達式中(包括if子句),排除了這種錯誤。一些編譯器如GNU編譯器集合(GCC),則在編譯if語句中包含賦值運算子的源碼時,提供了警告,雖然在if條件中可以有一些賦值的合法使用。在此情況下賦值語句必須對額外的括號特別聲明,以避免警告。

同樣地,一些語言如BASIC使用“=”等號同時代表賦值操作和相等關係兩者,因為在語法上它們是分開的(如Pascal,Ada,Python等,賦值運算子不能出現在表達式中)。

有些程序員習慣於逆向(一般從左到右條件判斷)寫一個常數的比較:

  if (2 == a) {   /* Mistaken use of = versus == would be a compile-time error */
  }

如果意外使用了=,因為 2 不是變量則源碼的編譯無效,編譯器會產生一個錯誤訊息,指出在等號的位置應該以適當的運算子替換。這種編程寫法被稱為左手比較或尤達條件式

下表列出了各種編程測試型別相等的不同機制:

Language Physical equality Structural equality Notes
ALGOL 68 a :=: b or a is b a = b when a and b are pointers
C, C++ a == b *a == *b when a and b are pointers
C# object.ReferenceEquals(a, b) a.Equals(b) The == operator defaults to ReferenceEquals, but can be overloaded to perform Equals instead.
Common Lisp (eq a b) (equal a b)
Go a == b reflect.DeepEqual(*a, *b) when a and b are pointers
Java a == b a.equals(b)
JavaScript a === b a == b when a and b are two string objects containing equivalent characters, the === operator will still return true.
OCaml, Smalltalk a == b a = b
Pascal a^ = b^ a = b
Perl $a == $b $$a == $$b when $a and $b are references to scalars
PHP5 $a === $b $a == $b when $a and $b are objects
Python a is b a == b
Ruby a.equal?(b) a == b
Scheme (eq? a b) (equal? a b)
Swift a === b a == b when a and b have class type
Visual Basic .NET[inequality 1] a Is b or object.ReferenceEquals(a, b) a = b or a.Equals(b) Same as C#
Objective-C (Cocoa, GNUstep) a == b [a isEqual:b] when a and b are pointers to objects that are instances of NSObject
  1. ^ Patent application: On May 14, 2003, US application 20,040,230,959  "IS NOT OPERATOR" was filed for the ISNOT operator by employees of Microsoft. This patent was granted on November 18, 2004.

另見

编辑

参考

编辑