给出两个字符串a
和b
,有时我想确定三个语句之一:a < b
,a == b
或a > b
是正确的。
在像C或C ++这样的语言中,一次调用相应的函数或方法后,我将得到一个整数值v
。然后,我可以通过检查v < 0
,v == 0
还是v > 0
来确定上述哪种说法是正确的。
但是在Go中,我必须至少进行两次比较(例如,首先测试a < b
,然后测试a == b
),以找出这三个语句中哪一个是正确的。
我的问题是Go中有办法让我可以做一个比较吗?
事实证明,此功能称为three way comparison。
答案 0 :(得分:4)
即使此比较器功能存在于strings
软件包(strings.Compare()
)中,即使其文档也建议不要使用它:
Compare返回一个按字典顺序比较两个字符串的整数。如果a == b,结果将为0;如果a b,结果将为+1。
仅包含与包字节对称的比较信息。使用内置的字符串比较运算符==,<,>等,通常更清晰,总是更快。
strings.Compare()
不切实际?有几个原因。
首先,在这种Compare()
实用/通用的语言中,通常这些语言支持基于具有此签名的函数的排序。
例如,在Java中,您可以将Comparator
接口传递给Collections.sort()
。因此,在Java中,您不得不/执行这种比较(返回-1
,0
或1
)。
在Go中,排序不基于此类比较器功能。在Go语言中,排序是基于单个Less(i, j int) bool
函数的,该函数基本上是a[i] < a[j]
的比较,只是“少了吗?”。为此,您不需要strings.Compare()
,只需要a < b
。例如,请参见sort.Slice()
。
第二个原因:strings.Compare()
没有被有意优化 ,因此您不习惯使用它。 strings.Compare()
的实现有以下注释:
// NOTE(rsc): This function does NOT call the runtime cmpstring function,
// because we do not want to provide any performance justification for
// using strings.Compare. Basically no one should use strings.Compare.
// As the comment above says, it is here only for symmetry with package bytes.
// If performance is important, the compiler should be changed to recognize
// the pattern so that all code doing three-way comparisons, not just code
// using strings.Compare, can benefit.
这意味着a < b
比调用strings.Compare(a, b)
更快。
第三,strings.Compare()
的返回值是一个单个整数,其中包含以下信息:a
小于b
还是a
等于{{1} }或b
是否大于a
。如果确实需要使用所有三个分支(而不仅仅是“较少”或“等于”分支),则通常需要对b
的返回值进行进一步检查,如以下简单示例所示:
strings.Compare()
现在考虑一下:比较首先在switch strings.Compare("a", "b") {
case -1:
fmt.Println("less")
case 0:
fmt.Println("equal")
case 1: // or default:
fmt.Println("greater")
}
内部执行,然后在代码中再次进行比较(比较返回值)。这是多余的,而且性能较低。
上面的代码可以这样写(会更快):
strings.Compare()
如前所述,switch {
case a == b:
fmt.Println("equal")
case a < b:
fmt.Println("less")
default:
fmt.Println("greater")
}
并未针对性能进行优化。但是Go的排序库不需要strings.Compare()
,-1
,0
的结果来对字符串进行排序,只需要1
的结果即可,获取结果的效率与获取a < b
用其他语言的结果。
还要注意,Compare()
首先检查是否相等strings.Compare()
,并且只有在它们不相等时才检查a == b
。这很重要,因为Go中的a < b
值存储string
的长度(有关详细信息,请参见reflect.StringHeader
),这意味着如果2个字符串的长度不同,则可以立即确定它们的长度不相等。 C和C ++使用string
终止的字符串值,这意味着要知道两个字符串是否相等,即使一个字符串是一千个字符而另一个字符串少了一个,也总是需要比较整个字符串。实际上,这并不是完全正确的,因为如果比较字符时检测到不匹配,则比较结束,但是这仍然比比较2个整数慢很多。
答案 1 :(得分:3)
比较功能呢?
答案 2 :(得分:1)
Go是由程序员为程序员设计的。如果要使用C strcmp
函数,请在Go中编写一个。
例如,
package main
import "fmt"
func strcmp(s1, s2 string) int {
lens := len(s1)
if lens > len(s2) {
lens = len(s2)
}
for i := 0; i < lens; i++ {
if s1[i] != s2[i] {
return int(s1[i]) - int(s2[i])
}
}
return len(s1) - len(s2)
}
func main() {
tests := []struct {
s1, s2 string
cmp int
}{
{"", "", 0},
{"a", "a", 0},
{"a", "b", -1},
{"b", "a", +1},
{"a", "aa", -1},
{"aa", "a", 1},
}
for _, t := range tests {
cmp := strcmp(t.s1, t.s2)
fmt.Printf("%q %q %d %t\n", t.s1, t.s2, cmp, cmp == t.cmp)
}
}
游乐场:https://play.golang.org/p/EAzV5_ouDI2
输出:
"" "" 0 true
"a" "a" 0 true
"a" "b" -1 true
"b" "a" 1 true
"a" "aa" -1 true
"aa" "a" 1 true
GNU C库(glibc):strcmp.c