详解Go 语言中的比较操作符

这篇文章专注于 6 个操作符,==,!=,<,<=,> 和 >=。我们将深入探讨它们的语法和用法的细微差别。对很多人来说,这听起来不像是吸引人的事,或者他们可能已经从其他编程语言获得了糟糕的经验。然而,在 Go 中它们定义的很好并简洁。下面讨论的主题,如可比性将出现在其他场合,如 maps。为了使用上述操作符,至少有一个操作数需要可赋值给第二个操作数:

package main
import "fmt"
type T struct {
  name string
}
func main() {
  s := struct{ name string }{"foo"}
  t := T{"foo"}
  fmt.Println(s == t) // true
}

这条规则显著缩小了可选范围:

var a int = 1
var b rune = '1'
fmt.Println(a == b)

类似的代码在 Javascript 或 Python 中可以运行。但在 Go 中它是非法的,并且在编译时会被检测到。

src/github.com/mlowicki/lab/lab.go:8: invalid operation: a == b (mismatched types int and rune)

可赋值不是唯一要求。这是相等和顺序操作符的规则……

相等操作符

操作数需要使用 == 或 != 操作符进行比较。哪些方法,哪些值可以被比较?Go 规范定义的非常明确:

boolean 值可比较(如果俩个值都是真或假,那么比较结果被认为 true)
整数和浮点数比较:

var a int = 1
var b int = 2
var c float32 = 3.3
var d float32 = 4.4
fmt.Println(a == b) // false
fmt.Println(c == d) // false

当编译时 a == d 会抛出异常( int 和 float32 类型不匹配)因为它不可能用 int 和 float 比较。

复数相等,如果他们的是实数和虚数部分都相等:

var a complex64 = 1 + 1i
var b complex64 = 1 + 2i
var c complex64 = 1 + 2i
fmt.Println(a == b) // false
fmt.Println(b == c) // true

字符串类型值可比较

指针类型值相等,如果他们都是 nil 或都指向相同的变量:

type T struct {
  name string
}
func main() {
  t1 := T{"foo"}
  t2 := T{"bar"}
  p1 := &t1
  p2 := &t1
  p3 := &t2
  fmt.Println(p1 == p2)  // true
  fmt.Println(p2 == p3)  // false
  fmt.Println(p3 == nil) // false
}

不同的 zero-size 变量可能具有相同的内存地址,因此我们不假设任何指向这些变量的指针相等。

a1 := [0]int{}
a2 := [0]int{}
p1 := &a1
p2 := &a2
fmt.Println(p1 == p2) // might be true or false. Don't rely on it!

通道类型值相等,如果他们确实一样(被相同的内置 make 方法创建)或值都是 nil:

ch1 := make(chan int)
ch2 := make(chan int)
fmt.Println(ch1 == ch2) // false

接口类型是可比较。与通道和指针类型值比较一样,如果是 nil 或 动态类型和动态值是相同的:

type I interface {
  m()
}
type J interface {
  m()
}
type T struct {
  name string
}
func (T) m() {}
type U struct {
  name string
}
func (U) m() {}
func main() {
  var i1, i2, i3, i4 I
  var j1 J
  i1 = T{"foo"}
  i2 = T{"foo"}
  i3 = T{"bar"}
  i4 = U{"foo"}
  fmt.Println(i1 == i2) // true
  fmt.Println(i1 == i3) // false
  fmt.Println(i1 == i4) // false
  fmt.Println(i1 == j1) // false
}

比较接口类型的方法集不能相交。

接口类型 I 的 i 和 非接口类型 T 的 t 可比较,如果 T 实现了 I 则 T 类型的值是可比较的。如果 I 的 动态类型和 T 是相同的,并且 i 的动态值和 t 也是相同的,那么值是相等的:

type I interface {
  m()
}
type T struct{}
func (T) m() {}
type S struct{}
func (S) m() {}
func main() {
  t := T{}
  s := S{}
  var i I
  i = T{}
  fmt.Println(t == i) // true
  fmt.Println(s == i) // false
}
结构类型可比较,所以字段都需要比较。所有非空白字段相等则他们等。
a := struct {
  name string
  _ int32
}{name: "foo"}
b := struct {
  name string
  _ int32
}{name: "foo"}
fmt.Println(a == b) // true

Go 中 数组是同质的 —— 只有同一类型(数组元素类型)的值可以被存储其中。对于数组值比较,它们的元素类型需要可比较。如果对应的元素相同,数组就相等。

就是这样。上面列表很长但并不充满惊奇。尝试了解它在 JavaScript 是如何工作的……

有三种类型不能比较 —— maps, slices 和 functions。Go 编译器不允许这样做,并且编译比较 maps 的程序会引起一个错误 map can only be compared to nil. 。展示的错误告诉我们至少可以用 maps,slices 或 functions 和 nil 比较。

目前为止,我们知道接口值是可比较的,但 maps 是不可以的。如果接口值的动态类型是相同的,但是不能比较(如 maps),它会引起一个运行时错误:

type T struct {
  meta map[string]string
}
func (T) m() {}
func main() {
  var i1 I = T{}
  var i2 I = T{}
  fmt.Println(i1 == i2)
}
panic: runtime error: comparing uncomparable type main.T
goroutine 1 [running]:
panic(0x8f060, 0x4201a2030)
  /usr/local/go/src/runtime/panic.go:500 +0x1a1
main.main()
  ...

顺序操作符

这些操作符只能应用在三种类型:整数,浮点数和字符串类型。这没有什么特别的或 Go 特有的。值得注意的是字符串是按字典顺序排列的。byte-wise 一次一个字节并没有 Collation 算法。

fmt.Println("aaa" < "b") // true
fmt.Println("ł" > "z")  // true

结果

任何比较操作符的结果都是无类型布尔常量(true 或 false)。因为它没有类型,所以可以分配了给任何布尔变量:

var t T = true
t = 3.3 < 5
fmt.Println(t)

这段代码输出 true。另一个,尝试分配 bool 类型的值:

var t T = true
var b bool = true
t = b
fmt.Println(t)

产生一个错误,不能使用 b (bool类型)分配给 T 类型。

总结

以上所述是小编给大家介绍的Go 语言中的比较操作符,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 基于MongoDB数据库的数据类型和$type操作符详解

    前面的话 本文将详细介绍MongoDB数据库的数据类型和$type操作符 类型 数字 备注 Double 1 双精度浮点数 - 此类型用于存储浮点值 String 2 字符串 - 这是用于存储数据的最常用的数据类型.MongoDB中的字符串必须为UTF-8 Object 3 对象 - 此数据类型用于嵌入式文档 Array 4 数组 - 此类型用于将数组或列表或多个值存储到一个键中 Binary data 5 二进制数据 - 此数据类型用于存储二进制数据 Undefined 6 已废弃 Objec

  • MongoDB 管道的介绍及操作符实例

    MongoDB 管道的介绍及操作符实例 一 介绍 管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数. MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理.管道操作是可以重复的. 表达式:处理输入文档并输出.表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档. 这里我们介绍一下聚合框架中常用的几个操作: $project:修改输入文档的结构.可以用来重命名.增加或删除域,也可以用于创建计算结果以及嵌套文档. $m

  • 详解Go 语言中的比较操作符

    这篇文章专注于 6 个操作符,==,!=,<,<=,> 和 >=.我们将深入探讨它们的语法和用法的细微差别.对很多人来说,这听起来不像是吸引人的事,或者他们可能已经从其他编程语言获得了糟糕的经验.然而,在 Go 中它们定义的很好并简洁.下面讨论的主题,如可比性将出现在其他场合,如 maps.为了使用上述操作符,至少有一个操作数需要可赋值给第二个操作数: package main import "fmt" type T struct { name string }

  • 详解C语言中结构体的使用

    目录 结构体的声明 结构体成员的类型 结构体成员的访问 结构体的声明 结构体的定义:结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量. 举例: //定义结构体类型 struct tag//struct结构体关键字 tag结构体标签 struct tag结构体类型 { //成员变量 char name[20]; short age; char telphone[12]; char sex[5]; }s1,s2,s3;//s1,s2,s3是三个全局结构体变量 int m

  • 详解Go语言中关于包导入必学的 8 个知识点

    1. 单行导入与多行导入 在 Go 语言中,一个包可包含多个 .go 文件(这些文件必须得在同一级文件夹中),只要这些 .go 文件的头部都使用 package 关键字声明了同一个包. 导入包主要可分为两种方式: 单行导入 import "fmt" import "sync" 多行导入 import( "fmt" "sync" ) 如你所见,Go 语言中 导入的包,必须得用双引号包含,在这里吐槽一下. 2. 使用别名 在一些场

  • 详解R语言中生存分析模型与时间依赖性ROC曲线可视化

    R语言简介 R是用于统计分析.绘图的语言和操作环境.R是属于GNU系统的一个自由.免费.源代码开放的软件,它是一个用于统计计算和统计制图的优秀工具. 人们通常使用接收者操作特征曲线(ROC)进行二元结果逻辑回归.但是,流行病学研究中感兴趣的结果通常是事件发生时间.使用随时间变化的时间依赖性ROC可以更全面地描述这种情况下的预测模型. 时间依赖性ROC定义 令 Mi为用于死亡率预测的基线(时间0)标量标记. 当随时间推移观察到结果时,其预测性能取决于评估时间 t.直观地说,在零时间测量的标记值应该

  • 详解R语言中的多项式回归、局部回归、核平滑和平滑样条回归模型

    在标准线性模型中,我们假设 .当线性假设无法满足时,可以考虑使用其他方法. 多项式回归 扩展可能是假设某些多项式函数, 同样,在标准线性模型方法(使用GLM的条件正态分布)中,参数  可以使用最小二乘法获得,其中  在  . 即使此多项式模型不是真正的多项式模型,也可能仍然是一个很好的近似值 .实际上,根据 Stone-Weierstrass定理,如果  在某个区间上是连续的,则有一个统一的近似值  ,通过多项式函数. 仅作说明,请考虑以下数据集 db = data.frame(x=xr,y=y

  • 详解R语言中的表达式、数学公式、特殊符号

      在R语言的绘图函数中,如果文本参数是合法的R语言表达式,那么这个表达式就被用Tex类似的规则进行文本格式化. y <- function(x) (exp(-(x^2)/2))/sqrt(2*pi) plot(y, -5, 5, main = expression(f(x) == frac(1,sqrt(2*pi))*e^(-frac(x^2,2))), lwd = 3, col = "blue") library(ggplot2) x <- seq(0, 2*pi, b

  • 详解C语言中不同类型的数据转换规则

    不同类型数据间的混合运算与类型转换 1.自动类型转换 在C语言中,自动类型转换遵循以下规则: ①若参与运算量的类型不同,则先转换成同一类型,然后进行运算 ②转换按数据长度增加的方向进行,以保证精度不降低.如int型和long型运算时,先把int量转成long型后再进行运算 a.若两种类型的字节数不同,转换成字节数高的类型 b.若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型 ③所有的浮点运算都是以双精度进行的,即使是两个float单精度量运算的表达式,也要先转换成double

  • 详解C语言中二分查找的运用技巧

    目录 基础的二分查 查找左侧边界 查找右侧边界 二分查找问题分析 实例1: 爱吃香蕉的珂珂 实例2:运送包裹 前篇文章聊到了二分查找的基础以及细节的处理问题,主要介绍了 查找和目标值相等的元素.查找第一个和目标值相等的元素.查找最后一个和目标值相等的元素 三种情况. 这些情况都适用于有序数组中查找指定元素 这个基本的场景,但实际应用中可能不会这么直接,甚至看了题目之后,都不会想到可以用二分查找算法来解决 . 本文就来分析下二分查找在实际中的应用,通过分析几个应用二分查找的实例,总结下能使用二分查

  • 详解Go语言中的数据类型及类型转换

    目录 1.基本数据类型 2.基础数据类型转换 3.基本数据类型转为字符串 4.strconv的使用 5.字符串转为基础类型 1.基本数据类型 数据类型有很多,先研究一下基础的,例如:布尔型.数字类型.字符串类型. 数字类型有uint8.uint16.uint32.uint64.int8.int16.int32.int64(uint和int区别在于uint为无符号整数,即只支持正数,不支持负数形式) 数字浮点型有fload32.float64.complex64.complex126(后面两个均为

  • 详解Go语言中的作用域和变量隐藏

    目录 前言 包隐藏 全局变量 类型强制 闭包 := 的情况 总结 前言 变量隐藏在 Go 中可能会令人困惑,让我们尝试弄清楚. package main import ( "fmt" "io/ioutil" "log" ) func main() { f, err := ioutil.TempFile("", "") if err != nil { log.Fatal(err) } defer f.Clos

随机推荐