go語(yǔ)言的比較運(yùn)算

首先區(qū)分幾個(gè)概念:變量可比較,可排序,可賦值

可賦值

規(guī)范里面對(duì)賦值是這么定義的:https://golang.org/ref/spec#Assignability

A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:

  • x's type is identical to T.
  • x's type V and T have identical underlying types and at least one of V or T is not a defined type.
  • T is an interface type and x implements T.
  • x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a defined type.
  • x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
  • x is an untyped constant representable by a value of type T.

概括起來(lái)就是他們的類型需要滿足某種條件,或者類型相同,或者底層類型(underlying types)相同。

可比較

規(guī)范里面對(duì)比較操作是這么定義的:https://golang.org/ref/spec#Comparison_operators

可比較又可以分為兩個(gè)小類

  1. 可比較,包括相等(==),和不相等(!=)
  2. 可排序,包括大于(>),大于等于(>=),小于(>),小于等于(<=)

可排序的一定是可比較的,反之不成立,即可比較的不一定是可排序的,例如struct類型就是可比較的,但不可排序。

  1. 可排序的數(shù)據(jù)類型有三種,Integer,F(xiàn)loating-point,和String
  2. 可比較的數(shù)據(jù)類型除了上述三種外,還有Boolean,Complex,Pointer,Channel,Interface,Struct,和Array
  3. 不可比較的數(shù)據(jù)類型包括,Slice, Map, 和Function

上述規(guī)范里面對(duì)哪種數(shù)據(jù)類型如何進(jìn)行比較,如何相等都做了描述,不細(xì)說(shuō),請(qǐng)參考原文。

至于如何定義他們相等的規(guī)則,也請(qǐng)參考上述規(guī)范文檔。

可賦值和可比較的關(guān)系

規(guī)范里是這么說(shuō)的:

In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.

也就是說(shuō)如果兩個(gè)變量可比較,那么他們必然是可賦值的,要么左邊變量可賦值給右邊變量,要么右邊變量可賦值給左邊變量。反之則不一定,即可賦值的變量,不一定可比較,比如前面提到的map類型變量。

所以兩個(gè)可比較的變量,也必須滿足他們或者類型相同,或者他們的底層類型(underlying types)相同。

兩個(gè)變量是否可比較這個(gè)規(guī)則是在編譯的時(shí)候由編譯器負(fù)責(zé)靜態(tài)檢查的。

舉例struct類型的比較

基本類型變量的比較很直觀,不在展開討論,這里我們舉幾個(gè)struct類型的比較的例子來(lái)說(shuō)明struct的比較。
注意這里指的是相等比較,而不是排序比較,因?yàn)閟truct不是可排序的。

規(guī)范里面對(duì)struct比較的規(guī)則定義:

Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.

例子1:類型是否相同問題

package main

import "fmt"

type T1 struct { name string }
type T2 struct { name string }

func main() {
    v11 := T1 { "foo" }
    v12 := T1 { "foo" }
    v21 := T2 { "foo" }
    v22 := T2 { "foo" }

    fmt.Printf("v11 == v12 is %v\n", v11 == v12)    // output: v11 == v12 is true
  //fmt.Printf("v11 == v21 is %v\n", v11 == v21)    // compile error, invalid operation: v11 == v21 (mismatched types T1 and T2)
  //fmt.Printf("v11 == v22 is %v\n", v11 == v22)    // compile error, invalid operation: v11 == v22 (mismatched types T1 and T2)

  //fmt.Printf("v12 == v21 is %v\n", v12 == v21)    // compile error, invalid operation: v12 == v21 (mismatched types T1 and T2)
  //fmt.Printf("v12 == v22 is %v\n", v12 == v22)    // compile error, invalid operation: v12 == v22 (mismatched types T1 and T2)

    fmt.Printf("v21 == v22 is %v\n", v21 == v22)    // output: v21 == v22 is true
}

這個(gè)例子說(shuō)明,struct類型不相同時(shí),他們是不可進(jìn)行比較的,編譯器在編譯的時(shí)候靜態(tài)檢查類型;此例中變量v1x和v2x的類型不相同,一個(gè)是T1,另一個(gè)是T2,所以他們不能進(jìn)行比較,雖然他們的內(nèi)部底層類型一樣,因?yàn)門1和T2的定義內(nèi)容是一樣的,但是go認(rèn)定他們是不同的類型。

因?yàn)檫@違背了可比較的第一個(gè)限定條件,即變量必須是可賦值的;T1和T2不是可相互賦值的類型。

關(guān)于類型相同判斷的問題,再舉一個(gè)例子:

package main

import "fmt"
 
type Int int

func main() {
    var v11 int = 1
    var v12 int = 1
    var v21 Int = 1
    var v22 Int = 1
     
    fmt.Printf("v11 == v12 is %v\n", v11 == v12)    // output: v11 == v12 is true
  //fmt.Printf("v11 == v21 is %v\n", v11 == v21)    // compile error, invalid operation: v11 == v21 (mismatched types int and Int)
  //fmt.Printf("v11 == v22 is %v\n", v11 == v22)    // compile error, invalid operation: v11 == v22 (mismatched types int and Int)

  //fmt.Printf("v12 == v21 is %v\n", v12 == v21)    // compile error, invalid operation: v12 == v21 (mismatched types int and Int)
  //fmt.Printf("v12 == v22 is %v\n", v12 == v22)    // compile error, invalid operation: v12 == v22 (mismatched types int and Int)

    fmt.Printf("v21 == v22 is %v\n", v21 == v22)    // output: v21 == v22 is true
}

這個(gè)例子中我們定義了一種新數(shù)據(jù)類型Int,雖然實(shí)際上他就是int,Int只是int的一個(gè)wrapper,go語(yǔ)言還是認(rèn)為他們是不同的數(shù)據(jù)類型。

例子2:是否所有的域(field)都可比較

package main

import "fmt"

type T1 struct { name string }
type T2 struct { name string; attrs map[string]interface{} }

func main() {
    v11 := T1 { "foo" }
    v12 := T1 { "foo" }
    v21 := T2 { "foo", make(map[string]interface{}) }
    v22 := T2 { "foo", make(map[string]interface{}) }

    fmt.Printf("v11 == v12 is %v\n", v11 == v12)    // output: v11 == v12 is true
    fmt.Printf("v21 == v22 is %v\n", v21 == v22)    // compile error: invalid operation: v21 == v22 (struct containing map[string]interface {} cannot be compared)
}

按照規(guī)范描述類型T2是否可比較需要它的所有域都是可比較的,這里因?yàn)門2含有一個(gè)attrs域,其類型是map,而map是不可比較的,所以T2不可比較。

例子3:包含空域(Blank Field)

package main

import "fmt"

type T1 struct { 
    i int64
    j int32
    _ int32
}

// About blank field:
// You cannot set or get a blank field; it cannot be refered.
// You can't do it in a composite literal either.
// The only use for a blank field in a struct is for padding.

func main() {
    v11 := T1 { i:10, j:10 }
    v12 := T1 { i:10, j:10 }

    fmt.Printf("v11 == v12 is %v\n", v11 == v12)    // output: v11 == v12 is true
}

這個(gè)例子使用了blank field,可見struct在比較的時(shí)候是丟棄blank field的,不管blank field的值是什么;進(jìn)而我們猜測(cè),go語(yǔ)言內(nèi)部比較struct類型的邏輯是遍歷遞歸所有的域,針對(duì)每個(gè)域分別比較,當(dāng)所有的遞歸域都返回true時(shí),就返回true,當(dāng)任何一個(gè)返回false時(shí),就返回false;可見struct并不是比較對(duì)象地址,也不是比較對(duì)象內(nèi)存塊值,而是一個(gè)一個(gè)域遍歷遞歸比較的,而blank field不可以引用,因而不參與比較。

例子4:匿名類型比較

go語(yǔ)言定義了兩種類型:命名類型,和匿名類型。

package main

import "fmt"
import "reflect"

type T1 struct { name string }
type T2 struct { name string }

func main() {
    v1 := T1 { "foo" }
    v2 := T2 { "foo" }
    v3 := struct{ name string } {"foo"}
    v4 := struct{ name string } {"foo"}

    fmt.Println("v1: type=", reflect.TypeOf(v1), "value=", reflect.ValueOf(v1)) // v1: type= main.T1 value= {foo}
    fmt.Println("v2: type=", reflect.TypeOf(v2), "value=", reflect.ValueOf(v2)) // v2: type= main.T2 value= {foo}
    fmt.Println("v3: type=", reflect.TypeOf(v3), "value=", reflect.ValueOf(v3)) // v3: type= struct { name string } value= {foo}
    fmt.Println("v4: type=", reflect.TypeOf(v4), "value=", reflect.ValueOf(v4)) // v4: type= struct { name string } value= {foo}

    //fmt.Println(v1 == v2) // compiler error: invalid operation: v1 == v2 (mismatched types T1 and T2)
    fmt.Println(v1 == v3)   // true, why? their type is different
    fmt.Println(v2 == v3)   // true, why?
    fmt.Println(v3 == v4)   // true
}

這個(gè)地方比較好理解的是v1和v2是不同的類型,一個(gè)是T1一個(gè)是T2,前面我們講過雖然T1和T2底層類型一樣,但是go認(rèn)為他們就是不同的類型。
然后v3和v4也好理解,他們的類型是一樣的匿名類型。
不好理解的是v1和v3,v2和v3明明他們的類型是不一樣的,為什么輸出true呢?

要回答這個(gè)問題,我們還是回到規(guī)范定義上面

Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.

關(guān)于struct是否可比較,只看一點(diǎn),是不是他的所有域都是可比較的,在這個(gè)例子總,只有一個(gè)域即name string,它是可比較的,所以這一條是滿足的,即此struct是可比較的。

再看規(guī)范里的另一條定義,這條定義是針對(duì)通用變量的,不只是struct

In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.

只有這條規(guī)則也能滿足的時(shí)候,兩個(gè)變量才可以比較;在我們例子中v1和v2就不滿足這條,所有不可比較,而v3和v4是滿足這條的,所有v3和v4是可比較的。

總結(jié):struct的比較

struct的比較只需要滿足兩個(gè)條件:

  1. 從所有比較操作繼承下來(lái)的規(guī)則,即兩個(gè)變量必須是可賦值的。
  2. 針對(duì)struct本身的規(guī)則,即struct的所有域必須都是可比較的;注意這里并不管struct本身的定義類型。

只要滿足這兩個(gè)條件,struct就是可比較的;可見并沒有限定兩個(gè)struct的類型必須一致,從而解釋了命名類型和匿名類型struct的比較規(guī)則,就是它并不管名字,反之都是struct類型就行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容