回答:
どちらのスタイルも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
どちらも慣用的であり、十分に明確です。それはより個人的な好みと明快さの問題です。
Russ Coxはgolang-nutsスレッドに書き込みます:
コードを明確にするもの。
要素xを見ようとしている場合
、x == 0の場合でも、通常len(s)> x
と記述しますが、「この特定の文字列であるか」に関心がある場合、s == ""と記述しがちです。成熟したコンパイラが
len(s)== 0とs == ""を同じ効率的なコードにコンパイルすると想定するのは妥当です。
...コードを明確にします。
Timmmmの回答で指摘されているように、Goコンパイラはどちらの場合でも同じコードを生成します。
len(v) > 0
で参照していました。golang.org/x/net/http2から生成されているため、自動的には表示されません。
これは時期尚早のマイクロ最適化のようです。コンパイラーは、両方の場合、または少なくともこれら2つの場合に同じコードを自由に生成できます。
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!")
}
}
TrimSpace
は、元の文字列から新しい文字列を割り当ててコピーするので、このアプローチは大規模な非効率をもたらします。
s
文字列型s[0:i]
が指定され、新しいコピーが返された場合にのみ当てはまります。Goでは文字列は不変なので、ここでコピーを作成する必要がありますか?
strings.TrimSpace( s )
文字列をトリミングする必要がない場合、新しい文字列の割り当てと文字のコピーは行われませんが、文字列をトリミングする必要がある場合は、余分なコピー(空白文字なし)が呼び出されます。
gocritic
リンターは、使用することを提案しているstrings.TrimSpace(str) == ""
長さのチェックを代わりに。
空のスペースとすべての先頭と末尾の空白を削除する必要があると仮定します。
import "strings"
if len(strings.TrimSpace(s)) == 0 { ... }
なぜなら:
len("") // is 0
len(" ") // one empty space is 1
len(" ") // two empty spaces is 2
< 1
+1にすることです
現在のところ、Goコンパイラはどちらの場合も同じコードを生成するため、好みの問題です。GCCGoは異なるコードを生成しますが、ほとんど誰もがそれを使用しないので、私はそれについて心配しません。
以下のような関数を使用すると、よりクリーンでエラーが発生しにくくなります。
func empty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
コメントに追加するだけです
主にパフォーマンステストの方法について。
私は次のコードでテストを行いました:
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.01ns / op)。
そして、完全なログを見ると、試行間の差はベンチマーク関数間の差よりも大きくなります。
また、BenchmarkStringCheckEqとBenchmarkStringCheckNeまたはBenchmarkStringCheckLenとBenchmarkStringCheckLenGtの間には、後者のバリアントを2回ではなく6回含める必要があるとしても、測定可能な違いはないようです。
変更されたテストまたは内部ループを使用してテストを追加することにより、同等のパフォーマンスについてある程度の信頼を得ることができます。これはより高速です:
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)を生成することも良いでしょう。また、可変長もあります。
したがって、空の文字列をgoでテストするための主要なメソッド間のパフォーマンスの違いに自信はありません。
そして、私は自信を持って述べることができます。空の文字列をテストするよりも空の文字列をテストしない方が速いです。また、1文字の文字列(プレフィックスバリアント)をテストするよりも、空の文字列をテストする方が高速です。
公式ガイドラインに従い、パフォーマンスの観点から、それらは同等であるように見えます(ANisus回答)。s!= ""の方が構文上の利点があるためより優れています。s!= ""は、変数が文字列でない場合、コンパイル時に失敗しますが、他のいくつかのデータ型ではlen(s)== 0が渡されます。
len()
、ほんの少しだけ余分な作業が必要です。ただし、Cで使用していたことの1つは、左側をaにキャストするconst
か、演算子の左側に静的文字列を配置して、s == ""がs = ""になるのを防ぎ、C構文では許容されるようにしました。 ..そしておそらくgolangも。(拡張ifを参照)
これは、少なくとも1つのスペース以外の文字が存在するかどうかを確認するだけでよいため、文字列全体をトリミングするよりもパフォーマンスが高くなります。
// 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はlen zeroでチェックしています
空の文字列チェックと空でない文字列チェックを使用してチェックします。空の文字列を使用したチェックの方が高速であることがわかります。
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 {
}
}
}
if mystring != "" { }
今日、最もよく、好まれ、慣用的な方法です。それ以外に標準ライブラリが含まれている理由は、len(mystring) == 0
最適化が理にかなっている2010年より前に作成されたためです。