A Tour of Goの練習問題を解説するシリーズ(7/11) – Exercise: Readers
みなさん、こんにちは。人類をGopherにしたいと考えているまるりんです。
A Tour of Goはプログラミング言語Goの入門サイトです。 このシリーズではA Tour of Goの練習問題を解説します。
今回は以下の問題を扱います。
問題
Exercise: Readers
解答
https://go.dev/play/p/tVGZy4D70SP
ASCII文字 'A' の無限ストリームを出力する Reader 型を実装してください。
問題の意味がよく分からなかったので、とりあえず'A'
を返してみました。
ソース
https://go.dev/play/p/3mkfz0hY94A
実行結果
got byte 0 at offset 0, want 'A'
どうやら変数b[0]
に'A'
を期待しているようです。
ソース
https://go.dev/play/p/tVGZy4D70SP
実行結果
OK!
なぜか「OK!」が出力されました。この問題が問うていることの真意はなんでしょうか。
まずreader.Validate()
の中を覗いてみたいと思います。
go getでダウンロードします。
$ go get golang.org/x/tour/reader
$ ls ~/go/pkg/mod/golang.org/x/tour@v0.1.0/reader/validate.go
以下がreader.Validate()
です。
func Validate(r io.Reader) {
b := make([]byte, 1024, 2048)
i, o := 0, 0
for ; i < 1<<20 && o < 1<<20; i++ { // test 1mb
n, err := r.Read(b)
for i, v := range b[:n] {
if v != 'A' {
fmt.Fprintf(os.Stderr, "got byte %x at offset %v, want 'A'\n", v, o+i)
return
}
}
o += n
if err != nil {
fmt.Fprintf(os.Stderr, "read error: %v\n", err)
return
}
}
if o == 0 {
fmt.Fprintf(os.Stderr, "read zero bytes after %d Read calls\n", i)
return
}
fmt.Println("OK!")
}
(よく考えたらGitHubにありました)
https://github.com/golang/tour/blob/master/reader/validate.go#L13
ループの終了条件はループが1048576(1<<20 == 2^20)回実行されるか、r.Read()
で読み込んだバイト数の合計が1048576(1<<20 == 2^20)より小さくないか、です。
bの長さは最大で1024あるのでr.Read()
がそれを1024回読み出せば、以下の条件が偽になりループを抜けます。
1048576(読み込んだバイト数の合計) < 1048576(読み込めるバイト数の上限)
a = 1024: r.Read()が1回に読み出せるバイト数の上限
b = 1024: ループ実行回数
1048576 = a * b
= 1024 * 1024
= 2^10 * 2^10
= 2^20
これを踏まえた上でr.Read()
が最大長を読み出せる解答を作ります。
ソース
https://go.dev/play/p/tVGZy4D70SP
実行結果
OK!
結論として「無限ストリーム」ではなくたかだか1KBのストリームだということが分かりました(関数型言語でこのような概念を無限ストリームというのかどうかは知りません)。