go語言的比較運算

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

可賦值

規(guī)范里面對賦值是這么定義的: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.

概括起來就是他們的類型需要滿足某種條件银还,或者類型相同熬词,或者底層類型(underlying types)相同惫搏。

可比較

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

可比較又可以分為兩個小類

  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ī)范里面對哪種數(shù)據(jù)類型如何進行比較腥沽,如何相等都做了描述逮走,不細說,請參考原文今阳。

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

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

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

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

也就是說如果兩個變量可比較,那么他們必然是可賦值的墓臭,要么左邊變量可賦值給右邊變量蘸鲸,要么右邊變量可賦值給左邊變量。反之則不一定窿锉,即可賦值的變量酌摇,不一定可比較,比如前面提到的map類型變量嗡载。

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

兩個變量是否可比較這個規(guī)則是在編譯的時候由編譯器負責靜態(tài)檢查的怯伊。

舉例struct類型的比較

基本類型變量的比較很直觀,不在展開討論判沟,這里我們舉幾個struct類型的比較的例子來說明struct的比較耿芹。
注意這里指的是相等比較,而不是排序比較挪哄,因為struct不是可排序的吧秕。

規(guī)范里面對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
}

這個例子說明,struct類型不相同時迹炼,他們是不可進行比較的砸彬,編譯器在編譯的時候靜態(tài)檢查類型;此例中變量v1x和v2x的類型不相同斯入,一個是T1砂碉,另一個是T2,所以他們不能進行比較刻两,雖然他們的內(nèi)部底層類型一樣增蹭,因為T1和T2的定義內(nèi)容是一樣的,但是go認定他們是不同的類型磅摹。

因為這違背了可比較的第一個限定條件滋迈,即變量必須是可賦值的;T1和T2不是可相互賦值的類型户誓。

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

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
}

這個例子中我們定義了一種新數(shù)據(jù)類型Int,雖然實際上他就是int帝美,Int只是int的一個wrapper碍彭,go語言還是認為他們是不同的數(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是否可比較需要它的所有域都是可比較的,這里因為T2含有一個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
}

這個例子使用了blank field,可見struct在比較的時候是丟棄blank field的抵知,不管blank field的值是什么墙基;進而我們猜測,go語言內(nèi)部比較struct類型的邏輯是遍歷遞歸所有的域刷喜,針對每個域分別比較残制,當所有的遞歸域都返回true時,就返回true掖疮,當任何一個返回false時初茶,就返回false;可見struct并不是比較對象地址浊闪,也不是比較對象內(nèi)存塊值恼布,而是一個一個域遍歷遞歸比較的,而blank field不可以引用搁宾,因而不參與比較折汞。

例子4:匿名類型比較

go語言定義了兩種類型:命名類型,和匿名類型盖腿。

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
}

這個地方比較好理解的是v1和v2是不同的類型爽待,一個是T1一個是T2,前面我們講過雖然T1和T2底層類型一樣翩腐,但是go認為他們就是不同的類型鸟款。
然后v3和v4也好理解,他們的類型是一樣的匿名類型茂卦。
不好理解的是v1和v3欠雌,v2和v3明明他們的類型是不一樣的,為什么輸出true呢疙筹?

要回答這個問題富俄,我們還是回到規(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是否可比較,只看一點而咆,是不是他的所有域都是可比較的霍比,在這個例子總,只有一個域即name string暴备,它是可比較的悠瞬,所以這一條是滿足的,即此struct是可比較的。

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

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

只有這條規(guī)則也能滿足的時候,兩個變量才可以比較凌外;在我們例子中v1和v2就不滿足這條辩尊,所有不可比較,而v3和v4是滿足這條的康辑,所有v3和v4是可比較的摄欲。

總結(jié):struct的比較

struct的比較只需要滿足兩個條件:

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末补履,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子剿另,更是在濱河造成了極大的恐慌箫锤,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雨女,死亡現(xiàn)場離奇詭異谚攒,居然都是意外死亡,警方通過查閱死者的電腦和手機氛堕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門馏臭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人讼稚,你說我怎么就攤上這事括儒。” “怎么了锐想?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵帮寻,是天一觀的道長。 經(jīng)常有香客問我赠摇,道長固逗,這世上最難降的妖魔是什么浅蚪? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮烫罩,結(jié)果婚禮上惜傲,老公的妹妹穿的比我還像新娘。我一直安慰自己贝攒,他們只是感情好盗誊,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著饿这,像睡著了一般浊伙。 火紅的嫁衣襯著肌膚如雪撞秋。 梳的紋絲不亂的頭發(fā)上长捧,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音吻贿,去河邊找鬼串结。 笑死,一個胖子當著我的面吹牛舅列,可吹牛的內(nèi)容都是我干的肌割。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼帐要,長吁一口氣:“原來是場噩夢啊……” “哼把敞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起榨惠,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤奋早,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后赠橙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耽装,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年期揪,在試婚紗的時候發(fā)現(xiàn)自己被綠了掉奄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡凤薛,死狀恐怖姓建,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缤苫,我是刑警寧澤引瀑,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站榨馁,受9級特大地震影響憨栽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一屑柔、第九天 我趴在偏房一處隱蔽的房頂上張望屡萤。 院中可真熱鬧,春花似錦掸宛、人聲如沸死陆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽措译。三九已至,卻和暖如春饰序,著一層夾襖步出監(jiān)牢的瞬間领虹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工求豫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留塌衰,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓蝠嘉,卻偏偏與公主長得像最疆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蚤告,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容