Yanıtlar:
Her iki stil de Go'nun standart kitaplıklarında kullanılır.
if len(s) > 0 { ... }
bulunabilir strconv
paketinin: http://golang.org/src/pkg/strconv/atoi.go
if s != "" { ... }
bulunabilir encoding/json
paketinin: http://golang.org/src/pkg/encoding/json/encode.go
Her ikisi de deyimsel ve yeterince açık. Daha çok kişisel zevk ve netlik meselesidir.
Russ Cox bir golang-fındık parçasında yazıyor :
Kodu netleştiren.
Eleman x'e bakmak
üzereysem, x == 0 için bile genellikle len (s)> x yazıyorum , ama eğer
"bu belirli dize" umursam "s ==" yazma eğilimindeyim "".Yetişkin bir derleyicinin
len (s) == 0 ve s == "" aynı, verimli koda derleyeceğini varsaymak mantıklıdır .
...Kodu netleştirin.
İşaret edildiği gibi Timmmm cevabı , Go derleyici her iki durumda da aynı kod oluşturmak gelmez.
len(v) > 0
olarak h2_bundle.go (hat 2702). Golang.org/x/net/http2 adresinden üretildiği için otomatik olarak gösterilmiyor.
Bu erken mikrooptimizasyon gibi görünüyor. Derleyici her iki durum için veya en azından bu ikisi için aynı kodu üretmekte serbesttir
if len(s) != 0 { ... }
ve
if s != "" { ... }
çünkü anlambilim açıkça eşittir.
Uzunluğun kontrol edilmesi iyi bir yanıttır, ancak aynı zamanda yalnızca boşluk olan "boş" bir dizeyi de hesaba katabilirsiniz. "Teknik olarak" boş değil, ancak kontrol etmek isterseniz:
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!")
}
}
TrimSpace
orijinal dizeden yeni bir dize atar ve kopyalar, böylece bu yaklaşım ölçeğinde verimsizlikleri ortaya çıkarır.
s
dize türünde s[0:i]
yeni bir kopya döndürürse doğru olur . Go'da dizeler değiştirilemez, bu yüzden burada bir kopya oluşturması gerekiyor mu?
strings.TrimSpace( s )
dize kesilmesi gerekmiyorsa yeni dize ayırmaya ve karakter kopyasına neden olmaz, ancak dize kesilmesi gerekiyorsa, fazladan kopya (boşluk karakteri olmadan) çağrılır.
gocritic
Linter kullanarak önerir strings.TrimSpace(str) == ""
yerine uzunluk çek.
Boş alanların ve tüm önde gelen ve arkadaki beyaz alanların kaldırılması gerektiğini varsayarsak:
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
Çünkü :
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2
< 1
+1
Şu an itibariyle, Go derleyicisi her iki durumda da aynı kodu üretir, bu yüzden bir zevk meselesidir. GCCGo farklı kod üretir, ancak zar zor kimse kullanır, bu yüzden bu konuda endişe olmaz.
Aşağıdaki gibi bir işlevi kullanmak daha temiz ve daha az hataya açık olacaktır:
func empty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
Sadece daha eklemek için açıklama
Temelde performans testi nasıl yapılır.
Aşağıdaki kod ile test yaptım:
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)
}
}
Ve sonuçlar:
% 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
Etkili varyantlar genellikle en hızlı süreye ulaşmaz ve varyant üst hızı arasında sadece minimum fark (yaklaşık 0.01ns / op) vardır.
Tam günlüğe bakarsam, denemeler arasındaki fark, karşılaştırma işlevleri arasındaki farktan daha büyüktür.
Ayrıca, BenchmarkStringCheckEq ve BenchmarkStringCheckNe veya BenchmarkStringCheckLen ve BenchmarkStringCheckLenGt arasında, ikinci varyantların 2 kez yerine 6 kat artması halinde bile ölçülebilir bir fark yok gibi görünüyor.
Değiştirilmiş test veya iç döngü ile testler ekleyerek eşit performans hakkında biraz güven kazanmayı deneyebilirsiniz. Bu daha hızlı:
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)
}
}
Bu daha hızlı değil:
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)
}
}
Her iki varyant genellikle ana testler arasındaki farktan daha hızlı veya daha yavaştır.
Ayrıca, ilgili dağıtıma sahip dize üreteci kullanarak test dizeleri (ss) oluşturmak da iyi olur. Değişken uzunlukları da var.
Bu nedenle, boş dizeyi test etmek için ana yöntemler arasındaki performans farkına güvenmiyorum.
Ve biraz güvenle söyleyebilirim, boş dizeyi hiç test etmemek, boş dizeyi test etmekten daha hızlıdır. Ayrıca boş karakter dizisini test etmek, 1 karakter dizisini (önek varyantı) test etmekten daha hızlıdır.
Resmi yönergelere göre ve performans açısından eşdeğer görünüyorlar ( ANisus cevabı ), s! = "" Sözdizimsel bir avantaj nedeniyle daha iyi olurdu. Değişken bir dize değilse s! = "" derleme zamanında başarısız olurken, len (s) == 0 diğer birkaç veri türü için de geçer.
len()
bu kadar az iş gerektiriyor. Ancak, C'de yaptığımız bir şey, const
s == "" ın C sözdiziminde kabul edilebilir olan s = "" olmasını önlemek için sol tarafı a ya da statik dizeyi operatörün sol tarafına koymaktı. .. ve muhtemelen golang da. (uzatılmışsa bakınız)
Bu, tüm dizeyi kırpmaktan daha performanslı olacaktır, çünkü yalnızca en az bir tane boşluk olmayan karakteri kontrol etmeniz gerekir
// 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
}
Bence en iyi yol boş string ile karşılaştırmak
BenchmarkStringCheck1 boş dize ile kontrol ediyor
BenchmarkStringCheck2 sıfır ile kontrol ediyor
Boş ve boş olmayan dize denetimi ile kontrol ediyorum. Boş bir dize ile kontrol etmenin daha hızlı olduğunu görebilirsiniz.
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
kod
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 {
}
}
}
if mystring != "" { }
iyi, tercih ve deyimsel yolu BUGÜN. Standart kütüphanenin başka türlü içermesinin nedeni,len(mystring) == 0
optimizasyonun anlamlı olduğu 2010'dan önce yazılmış olmasıdır .