空文字列確認は長さをとるべきか?

TL;DR s == ""とlen(s) == 0は等価 文字列比較か、長さ比較か Go言語で文字列が空かどうかを調べるには次の二つの方法があります。 1 2 3 4 5 6 7 8 9 // 1: 文字列を空文字列と比較する if s == "" { // do something } // 2: 文字列の長さが0かどうか調べる if len(s) == "" { // do something } 標準パッケージ・サードパーティパッケージともに、どちらの書き方も散見されます。 どちらを使うのが良いのでしょうか? 答えはどちらでも良いだそうです。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package benchmark_test import "testing" var somethingString = "hogehogefugafuga" func BenchmarkCompareString(b *testing.B) { for i := 0; i < b.N; i++ { if somethingString == "" { } } } func BenchmarkCompareStringByLength(b *testing.B) { for i := 0; i < b.N; i++ { if len(somethingString) == 0 { } } } このコードに対して go tool compile -Sした結果が以下。 BenchmarkCompareStringとBenchmarkCompareStringByLengthでは同じ内容となっています。 ...

2019-10-09 · nasa9084

array/sliceに対する存在確認関数のベンチマーク

Pythonでいうところの、次の様な条件式を実現する関数を書きたかった。 1 2 3 4 5 ls = ["foo", "bar", "baz"] s = "baz" if s in ls: print("FOOBAR!") 対象がリストの時、普段なら普通にfor文を回すのですが、今回やりたかったのは定数値の一覧にあるかどうか、だったのと、定数の数も少なかったので、とりあえずで以下の様に実装していました。 1 2 3 4 5 func something(s string) error { if s != "foo" && s != "bar" && s != "baz" { return errors.New("value invalid") } } 流石に雑すぎるので、リファクタリングしよう、と思ったのですが、「はて、for文挟んだら遅くなったりしないだろうか」などと考えてしまったのでベンチマークを取りました。 TL; DR 素直にfor文を回しても大して問題はなさそう result 今回取ったベンチマークは6種類です。 for-range文を回す for文を回す map[string]struct{}を集合として取り扱ってみる &&, ||でつなぐ switch文を使う sort.SearchStrings()を使う 6番目のsort.SearchStrings()を使う方法はstackoverflow に書いてあった方法で、二分探索をしてくれるというのでやってみました。 結果は次の通り。 BenchmarkInByForRange-4 200000000 9.34 ns/op 0 B/op 0 allocs/op BenchmarkInByFor-4 100000000 10.1 ns/op 0 B/op 0 allocs/op BenchmarkInByMap-4 200000000 7.79 ns/op 0 B/op 0 allocs/op BenchmarkInByAnd-4 1000000000 2.85 ns/op 0 B/op 0 allocs/op BenchmarkInBySwitch-4 2000000000 1.39 ns/op 0 B/op 0 allocs/op BenchmarkInBySortSearchStrings-4 10000000 179 ns/op 32 B/op 1 allocs/op まぁ予想通りではあるものの、sort.SearchStrings()を使う方法は遅いですね。これはこの関数の「事前にリストがソート済みであること」という条件のために関数内でソートをしてるからだと思われます。(実際、ソート済みのリストを使って、関数内でソートをしないようにすると1/4くらいにはなる) ...

2018-06-26 · nasa9084

strings.Builderとbytes.BufferのWrite系関数のベンチマーク

TL; DR 平均して見るとstrings.Builder#WriteXXXの方が速そう strings.Builder Go 1.10からstrings.Builder構造体が追加されました。 公式ドキュメントには、 A Builder is used to efficiently build a string using Write methods. It minimizes memory copying. The zero value is ready to use. Do not copy a non-zero Builder. と説明が書かれています。 おそらく、これまで文字列の組み立てをする際にはbytes.Bufferを使っている場合が多かったと思われますが、そういった目的の選択肢として作られたようです。 が、説明を読んでもいまいち違いがわかりません。 とりあえず、bytes.Bufferとstrings.Builderでは速度面で違いがあるのか調べるべく、ベンチマークを実施しました。 条件 実行した環境 MacBook Air MacOS Sierra 10.12.6 CPU: Core i7 1.7GHz メモリ: 8GB 実行する関数 Write WriteByte WriteRune WriteString exec 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 $ for i in {1..10}; do go test -bench . | tail -n +4 | head -n 8 && echo; done BenchmarkBuilderWrite-4 10000000 129 ns/op BenchmarkBuiderWriteByte-4 200000000 6.47 ns/op BenchmarkBuilderWriteRune-4 200000000 7.61 ns/op BenchmarkBuilderWriteString-4 30000000 92.4 ns/op BenchmarkBufferWrite-4 50000000 196 ns/op BenchmarkBufferWriteByte-4 300000000 9.40 ns/op BenchmarkBufferWriteRune-4 200000000 8.18 ns/op BenchmarkBufferWriteString-4 50000000 217 ns/op BenchmarkBuilderWrite-4 10000000 189 ns/op BenchmarkBuiderWriteByte-4 100000000 10.5 ns/op BenchmarkBuilderWriteRune-4 100000000 10.4 ns/op BenchmarkBuilderWriteString-4 30000000 157 ns/op BenchmarkBufferWrite-4 50000000 238 ns/op BenchmarkBufferWriteByte-4 100000000 19.9 ns/op BenchmarkBufferWriteRune-4 100000000 15.5 ns/op BenchmarkBufferWriteString-4 30000000 422 ns/op BenchmarkBuilderWrite-4 10000000 131 ns/op BenchmarkBuiderWriteByte-4 200000000 7.58 ns/op BenchmarkBuilderWriteRune-4 200000000 8.48 ns/op BenchmarkBuilderWriteString-4 30000000 113 ns/op BenchmarkBufferWrite-4 50000000 199 ns/op BenchmarkBufferWriteByte-4 100000000 10.4 ns/op BenchmarkBufferWriteRune-4 200000000 12.0 ns/op BenchmarkBufferWriteString-4 50000000 382 ns/op BenchmarkBuilderWrite-4 10000000 122 ns/op BenchmarkBuiderWriteByte-4 200000000 7.45 ns/op BenchmarkBuilderWriteRune-4 200000000 8.44 ns/op BenchmarkBuilderWriteString-4 30000000 155 ns/op BenchmarkBufferWrite-4 50000000 264 ns/op BenchmarkBufferWriteByte-4 200000000 7.08 ns/op BenchmarkBufferWriteRune-4 200000000 10.1 ns/op BenchmarkBufferWriteString-4 30000000 413 ns/op BenchmarkBuilderWrite-4 20000000 117 ns/op BenchmarkBuiderWriteByte-4 200000000 6.81 ns/op BenchmarkBuilderWriteRune-4 200000000 6.87 ns/op BenchmarkBuilderWriteString-4 50000000 219 ns/op BenchmarkBufferWrite-4 20000000 101 ns/op BenchmarkBufferWriteByte-4 200000000 6.22 ns/op BenchmarkBufferWriteRune-4 200000000 12.2 ns/op BenchmarkBufferWriteString-4 50000000 513 ns/op BenchmarkBuilderWrite-4 10000000 161 ns/op BenchmarkBuiderWriteByte-4 200000000 8.36 ns/op BenchmarkBuilderWriteRune-4 200000000 8.24 ns/op BenchmarkBuilderWriteString-4 30000000 109 ns/op BenchmarkBufferWrite-4 50000000 296 ns/op BenchmarkBufferWriteByte-4 100000000 10.6 ns/op BenchmarkBufferWriteRune-4 100000000 11.4 ns/op BenchmarkBufferWriteString-4 50000000 484 ns/op BenchmarkBuilderWrite-4 10000000 133 ns/op BenchmarkBuiderWriteByte-4 200000000 7.16 ns/op BenchmarkBuilderWriteRune-4 200000000 8.10 ns/op BenchmarkBuilderWriteString-4 30000000 194 ns/op BenchmarkBufferWrite-4 50000000 190 ns/op BenchmarkBufferWriteByte-4 200000000 5.51 ns/op BenchmarkBufferWriteRune-4 200000000 8.72 ns/op BenchmarkBufferWriteString-4 50000000 281 ns/op BenchmarkBuilderWrite-4 10000000 136 ns/op BenchmarkBuiderWriteByte-4 200000000 11.4 ns/op BenchmarkBuilderWriteRune-4 50000000 28.0 ns/op BenchmarkBuilderWriteString-4 10000000 119 ns/op BenchmarkBufferWrite-4 20000000 144 ns/op BenchmarkBufferWriteByte-4 100000000 16.0 ns/op BenchmarkBufferWriteRune-4 200000000 8.43 ns/op BenchmarkBufferWriteString-4 50000000 248 ns/op BenchmarkBuilderWrite-4 10000000 130 ns/op BenchmarkBuiderWriteByte-4 200000000 7.83 ns/op BenchmarkBuilderWriteRune-4 200000000 7.13 ns/op BenchmarkBuilderWriteString-4 30000000 99.0 ns/op BenchmarkBufferWrite-4 50000000 202 ns/op BenchmarkBufferWriteByte-4 200000000 10.7 ns/op BenchmarkBufferWriteRune-4 100000000 13.8 ns/op BenchmarkBufferWriteString-4 50000000 452 ns/op BenchmarkBuilderWrite-4 10000000 146 ns/op BenchmarkBuiderWriteByte-4 200000000 7.89 ns/op BenchmarkBuilderWriteRune-4 200000000 8.24 ns/op BenchmarkBuilderWriteString-4 30000000 122 ns/op BenchmarkBufferWrite-4 50000000 248 ns/op BenchmarkBufferWriteByte-4 100000000 31.7 ns/op BenchmarkBufferWriteRune-4 100000000 25.4 ns/op BenchmarkBufferWriteString-4 30000000 413 ns/op Source ベンチマークスクリプトのソースは以下の様になっています。 ...

2018-03-05 · nasa9084

Go言語で文字列を処理する

Go言語のregexpパッケージを使用した正規表現の処理は、一般的なスクリプト言語の処理速度と同程度で、正規表現を使用しない処理に比べてパフォーマンスがよくありません1。 そのため、可能であるならregexpパッケージを使用しないようにすべきです。 しかし、すべての処理を自分で書くのは大変です。 標準パッケージにも文字列を処理する関数が数多く用意されています。 strings パッケージ stringsパッケージはその名の通り、文字列を取り扱うパッケージです。 UTF-8でエンコードされた文字列(普通の文字列)をそのまま取り扱います。 判別系 Contains 1 func Contains(s, substr string) bool Contains関数は、sの中にsubstrが存在するかどうかを返します。 Pythonで言うところのsubstr in sに相当します。 正規表現ならば、substrとのmatchで真偽値をとることに相当します。 example 1 2 3 4 5 fmt.Println(strings.Contains("hogefugapiyo", "fuga")) // Output: true fmt.Println(strings.Contains("hogefugapiyo", "foo")) // Output: false ContainsAny 1 func ContainsAny(s, chars string) bool ContainsAny関数はsの中に、charsに含まれる文字のいずれかが存在するかどうかを返します。 つまり、charsは文字列ですが、扱いとしては文字の配列であると考えた方が良いでしょう。 正規表現で表すなら、/[${chars}]/の様な表現と考えられます(${chars}は置き換える)。 example 1 2 3 4 5 fmt.Println(strings.ContainsAny("hogefugapiyo", "abcd")) // Output: true fmt.Println(strings.ContainsAny("hogefugapiyo", "1234")) // Output: false HasPrefix 1 func HasPrefix(s, prefix string) bool HasPrefix関数は、sの頭がprefixと等しいかどうかを判別します。 正規表現で^を使った文字列マッチに相当します。 Prefixなのでマッチさせたい文字列をつい第一引数に与えたくなりますが、第二引数がPrefixです。 example 1 2 3 4 5 fmt.Println(strings.HasPrefix("hogefugapiyo", "hoge")) // Output: true fmt.Println(strings.HasPrefix("hogefugapiyo", "piyo")) // Output: false HasSuffix 1 func HasSuffix(s, suffix string) bool HasSuffix関数は、HasPrefix関数と対になる関数で、sの末尾がsuffixと等しいかどうかを判別します。 正規表現で$を使った文字列マッチに相当します。 ...

2018-03-05 · nasa9084

テキストを列ごとにそろえて出力する

TL;DR: 標準パッケージtext/tabwriter を使用する コマンドラインツールで標準出力を良い感じにそろえて出力したい場合があります。 例えば、docker では、以下の様に出力されます。 1 2 3 4 $ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8caad461b4a4 redis "docker-entrypoint.s…" 5 days ago Up 5 days 0.0.0.0:6379->6379/tcp redis-svr 329c9f9be035 mysql "docker-entrypoint.s…" 5 days ago Up 5 days 0.0.0.0:3306->3306/tcp mysql-svr このような map、あるいは構造体の配列 の様なものを、きれいに表形式の様に列をそろえて出力したい場合に便利なのが標準パッケージのtext/tabwriter です。 その名の通り、タブ区切りの文字列を良い感じに出力してくれるパッケージです。 text/tabwriterで定義されているのはいくつかの設定用定数 と、Writer構造体 のみです。 Writerは(勿論)io.Writerインターフェース を実装しています。 使用方法は通常のio.Writerとはすこし変わっていて、最初にWriter.Init() で初期化し、任意回数Writer.Write() で書き込みをした後、Writer.Flush() で整形した文字列を出力します。 NewWriter() 関数はWriter構造体をnew()した後Init()するのと同等です。 Writer.Init()関数及びNewWriter()関数に与える引数は以下の様になっています。 名前 型 内容 output io.Writer Flush()したときの出力先 minwidth int 1セルあたりの最小幅(パディングを含む) tabwidth int タブ文字の幅(スペースの個数と等しい) padding int パディング1 padchar byte パディング文字2 flags int 調整用フラグ 最後の引数である、調整用フラグには0(標準状態)を与えるか、パッケージ定数 の論理和を用いて設定を与えます。 ...

2018-01-29 · nasa9084