Какой метод лучше (более идоматический) для тестирования непустых строк (в Go)?
if len(mystring) > 0 { }
Или:
if mystring != "" { }
Или что-то еще?
Какой метод лучше (более идоматический) для тестирования непустых строк (в Go)?
if len(mystring) > 0 { }
Или:
if mystring != "" { }
Или что-то еще?
Оба стиля используются в стандартных библиотеках Go.
if len(s) > 0 { ... }
можно найти в пакете strconv
: http://golang.org/src/pkg/strconv/atoi.go
if s != "" { ... }
можно найти в пакете encoding/json
: http://golang.org/src/pkg/encoding/json/encode.go
Оба идиоматичны и достаточно понятны. Это скорее вопрос личного вкуса и ясности.
Рассел Кокс пишет в теме о голангах:
Тот, который делает код понятным.
Если я собираюсь посмотреть на элемент x, я обычно пишу
len (s)> x, даже для x == 0, но если я забочусь о
msgstr "это конкретная строка" Я склонен писать s == "".Разумно предположить, что зрелый компилятор будет компилировать
len (s) == 0 и s == "" в один и тот же эффективный код.
...Сделайте код понятным.
Как указано в ответе Timmmm, компилятор Go генерирует идентичный код в обоих случаях.
Это, по-видимому, преждевременная микрооптимизация. Компилятор может создавать один и тот же код для обоих случаев или, по крайней мере, для этих двух
if len(s) != 0 { ... }
и
if s != "" { ... }
потому что семантика явно равна.
Проверка длины - хороший ответ, но вы также можете указать "пустую" строку, которая также является только пробелом. Не "технически" пуст, но если вы хотите проверить:
package main
import (
"fmt"
"strings"
)
func main() {
stringOne := "merpflakes"
stringTwo := " "
stringThree := ""
if len(strings.TrimSpace(stringOne)) == 0 {
fmt.Println("String is empty!")
}
if len(strings.TrimSpace(stringTwo)) == 0 {
fmt.Println("String two is empty!")
}
if len(stringTwo) == 0 {
fmt.Println("String two is still empty!")
}
if len(strings.TrimSpace(stringThree)) == 0 {
fmt.Println("String three is empty!")
}
}
Предполагая, что пустые места и все начальные и конечные пробелы должны быть удалены:
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
Потому что:
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2
На данный момент компилятор Go генерирует идентичный код в обоих случаях, так что это дело вкуса. GCCGo генерирует другой код, но почти никто не использует его, поэтому я бы не беспокоился об этом.
Просто чтобы добавить больше к комментарию
В основном о том, как проводить тестирование производительности.
Я провел тестирование со следующим кодом:
import (
"testing"
)
var ss = []string{"Hello", "", "bar", " ", "baz", "ewrqlosakdjhf12934c r39yfashk fjkashkfashds fsdakjh-", "", "123"}
func BenchmarkStringCheckEq(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if s == "" {
c++
}
}
}
t := 2 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
func BenchmarkStringCheckLen(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if len(s) == 0 {
c++
}
}
}
t := 2 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
func BenchmarkStringCheckLenGt(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if len(s) > 0 {
c++
}
}
}
t := 6 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
func BenchmarkStringCheckNe(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss {
if s != "" {
c++
}
}
}
t := 6 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
И результаты были:
% for a in $(seq 50);do go test -run=^$ -bench=. --benchtime=1s ./...|grep Bench;done | tee -a log
% sort -k 3n log | head -10
BenchmarkStringCheckEq-4 150149937 8.06 ns/op
BenchmarkStringCheckLenGt-4 147926752 8.06 ns/op
BenchmarkStringCheckLenGt-4 148045771 8.06 ns/op
BenchmarkStringCheckNe-4 145506912 8.06 ns/op
BenchmarkStringCheckLen-4 145942450 8.07 ns/op
BenchmarkStringCheckEq-4 146990384 8.08 ns/op
BenchmarkStringCheckLenGt-4 149351529 8.08 ns/op
BenchmarkStringCheckNe-4 148212032 8.08 ns/op
BenchmarkStringCheckEq-4 145122193 8.09 ns/op
BenchmarkStringCheckEq-4 146277885 8.09 ns/op
Эффективные варианты обычно не достигают самого быстрого времени, и разница между максимальной скоростью варианта минимальна (около 0,01 нс/оп).
И если я посмотрю полный журнал, разница между попытками будет больше, чем разница между контрольными функциями.
Кроме того, не существует какой-либо ощутимой разницы между BenchmarkStringCheckEq и BenchmarkStringCheckNe или BenchmarkStringCheckLen и BenchmarkStringCheckLenGt даже если последние варианты должны быть выполнены c 6 раз вместо 2 раз.
Вы можете попытаться получить некоторую уверенность в равной производительности, добавив тесты с измененным тестом или внутренним циклом. Это быстрее:
func BenchmarkStringCheckNone4(b *testing.B) {
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, _ = range ss {
c++
}
}
t := len(ss) * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
Это не быстрее:
func BenchmarkStringCheckEq3(b *testing.B) {
ss2 := make([]string, len(ss))
prefix := "a"
for i, _ := range ss {
ss2[i] = prefix + ss[i]
}
c := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, s := range ss2 {
if s == prefix {
c++
}
}
}
t := 2 * b.N
if c != t {
b.Fatalf("did not catch empty strings: %d != %d", c, t)
}
}
Оба варианта обычно быстрее или медленнее, чем разница между основными тестами.
Также было бы хорошо генерировать тестовые строки (ss), используя генератор строк с соответствующим распределением. И иметь переменную длину тоже.
Так что я не уверен в разнице в производительности между основными методами для проверки пустой строки в го.
И я могу с уверенностью сказать, что быстрее вообще не тестировать пустую строку, чем тестировать пустую строку. А также быстрее протестировать пустую строку, чем 1 строку символов (префиксный вариант).
Было бы чище и менее подвержено ошибкам использовать функцию, подобную приведенной ниже:
func empty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
Это будет более производительным, чем обрезка всей строки, поскольку вам нужно проверить только один существующий непробельный символ.
// Strempty checks whether string contains only whitespace or not
func Strempty(s string) bool {
if len(s) == 0 {
return true
}
r := []rune(s)
l := len(r)
for l > 0 {
l--
if !unicode.IsSpace(r[l]) {
return false
}
}
return true
}
Я думаю, что лучше всего сравнивать с пустой строкой
BenchmarkStringCheck1 проверяется с пустой строкой
BenchmarkStringCheck2 проверяет с нулевым длиной
Я проверяю с пустой и непустой строкой проверки. Вы можете видеть, что проверка с пустой строкой быстрее.
BenchmarkStringCheck1-4 2000000000 0.29 ns/op 0 B/op 0 allocs/op
BenchmarkStringCheck1-4 2000000000 0.30 ns/op 0 B/op 0 allocs/op
BenchmarkStringCheck2-4 2000000000 0.30 ns/op 0 B/op 0 allocs/op
BenchmarkStringCheck2-4 2000000000 0.31 ns/op 0 B/op 0 allocs/op
код
func BenchmarkStringCheck1(b *testing.B) {
s := "Hello"
b.ResetTimer()
for n := 0; n < b.N; n++ {
if s == "" {
}
}
}
func BenchmarkStringCheck2(b *testing.B) {
s := "Hello"
b.ResetTimer()
for n := 0; n < b.N; n++ {
if len(s) == 0 {
}
}
}