-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Kadai3 tomoyuki.kobayashi #36
base: master
Are you sure you want to change the base?
Kadai3 tomoyuki.kobayashi #36
Conversation
} | ||
|
||
// Yaml HACK 使用しているパッケージの使用でしょうがなくpublicにしている | ||
type Yaml struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QuestionContainer とかそういう感じの名前にすると良さげですね
この struct は YAML データをパースした結果を入れるものですが、保存している先は別に YAML じゃなくても問題ありません。例えば JSON とかでも構わないわけです。
この struct の本質はレベルごとの問題を格納しているものなので、それに沿った名前にするほうが可読性も上がると思います。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ありがとうございます。おっしゃる通りで、こいつが何のためにいるのか、がわかる名前じゃないとダメですね
そもそもQuestionerの所でこの構造を知っている必要もないので、名前を変えてインターフェイスとかで渡すようにしようかと
…nsan/dojo4 into kadai3-tomoyuki.kobayashi
} | ||
|
||
// Run ゲームを開始する | ||
func (c *constGame) Run() (<-chan string, <-chan string, <-chan [2]int) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// TODO [2]int に名前をつけてやったほうがよかった
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
コメントしましたので、参考にしていただければと思います
|
||
// NewQuizData クイズデータを生成する | ||
func NewQuizData(source io.Reader) (QuizData, error) { | ||
data, err := ioutil.ReadAll(source) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ioutil.ReadAllはメモリにすべて載せてしまうので、streamから読み出すようにすることでメモリを圧迫しなくなります
(今回の場合は大量のファイルを読み出したりしないため、あまり影響はないと思いますが)
var s QuizSource
if err := yaml.NewDecoder(r).Decode(&s); err != nil {
return nil, err
}
return &s, nil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
あー、Reader(ストリーム) -> buf(メモリ) -> struct だとメモリ余計に確保するから
Reader(ストリーム) -> struct にしましょうという事ですね。確かに
} | ||
|
||
func (q *questionContainer) GetNewWord(level int) string { | ||
rand.Seed(time.Now().UnixNano()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rand.Seed
は毎回セットする必要はないため、一度だけ呼んであげればよいです
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
確かに
} | ||
|
||
// NewGame Gameのコンストラクタです | ||
func NewGame(timeout int, source io.Reader, input io.Reader) (Game, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
同じ型の引数は型を省略できます
func NewGame(timeout int, source, input io.Reader) (Game, error) {...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
確かに。そちらの方が可読性高いですね
// Run ゲームを開始する | ||
func (c *constGame) Run() (<-chan string, <-chan string, <-chan [2]int) { | ||
tc := time.Duration(c.timeout) * time.Second | ||
// NOTE defer cancel() cancelするとDone条件閉じてゲーム終わっちゃう |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://golang.org/pkg/context/
Failing to call the CancelFunc leaks the child and its children until the parent is canceled or the timer fires.
大抵の場合、cancelはちゃんと呼んでほうが良いです
その上でここでcancelを呼べないのは、設計としてここで作成したcontextを扱うべきでないか、 Run
がブロックしないからだと思います
Run(ctx context.Context)
でキャンセル可能にすれば、ここでの作成は必要なくなり、Runを呼び出す側で管理できるようになると思います
ただ、その場合は ゲーム
が扱うべき時間制限が外の扱いになってしまうのが気持ち悪いかもしれませんが、そもそもRunという長時間実行される可能性のあるメソッドにはcontext.Contextを渡して、キャンセル可能にしておくのは悪くないと思います
もう一つ、 Run
をブロックさせるという方法ですが、このメソッドの中でcontextを作成した場合、このメソッドがreturnするときにはその処理は終わっていてほしいはずです(もし終わっていてほしくない場合はもはや別のcontextとして扱われるべき状況である可能性が高いはず)
したがって、Runはゲームが終了するまでブロックする形で実行されれば、ゲームの終了とcancelは同一のタイミングでよくなるため、このメソッドの中で defer cancel()
が実行できるようになるはずです
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CancelFunc cancels the child and its children, removes the parent's reference to the child
あー、Cancel呼んでやらないと、子コンテキストへの参照が生き続けるのか、それはマズイですね。。。
Run をブロックさせるという方法ですが、このメソッドの中でcontextを作成した場合、このメソッドがreturnするときにはその処理は終わっていてほしいはずです(もし終わっていてほしくない場合はもはや別のcontextとして扱われるべき状況である可能性が高いはず)
言われてみると、確かにそうですね
Contextを持っているRunは終わったけどそのContextを参照したgoroutineは元気に生きているというのはおかしい。。。
go func() { | ||
for { | ||
word := c.GetNewWord(c.nextLevel()) | ||
qCh <- word |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ここでブロックしてしまうと、問題がchanから読み込まれるまでcontextによるキャンセルが効かなくなるので、selectに含めるとより扱いやすくなると思います
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
そうですね。selectにこれを追加して、defaultをなくすのが良さそうです
ありがとうございます。
for { | ||
word := c.GetNewWord(c.nextLevel()) | ||
qCh <- word | ||
c.currentWord = word |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
この c.currentWord
の扱いについて、このgoroutineも参照して、answerのgoroutineでも isCorrect
のメソッドから参照しているのでおそらくraceしてしまいそうです(おそらくまず発生しないと思いますが)
go test -race
でテストを実行してみてください
おそらく発生する流れ
- aCh <- ansが実行される
- mainのforのblockが次に進むので、qChから読み込まれ、c.currentWordが更新される
- isCorrectで次の問題の答えと比較されて、間違いと判定されてしまう
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
おそらくまず発生しないと思いますが
テストで固定で複数行のstringをinputさせて動作させてみたら結果に揺れがあったので、なんでだろうと思っていましたが
多分こいつですね。。。普通に起きていました
} | ||
} | ||
// tmp配下にハッシュにマッチするフォルダがなければ作る | ||
dlTmp := filepath.Join("tmp", hash) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ioutilにTempDirというメソッドがあります
https://golang.org/pkg/io/ioutil/#TempDir
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ありがとうございます
} | ||
|
||
// 取得したデータをBufに読み出し | ||
buf, err := ioutil.ReadAll(dRes.Body) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ReadAllせずに io.Copy
をすれば十分かと思います
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ここも、ストリームはストリームのまま渡そう、という話ですね
ありがとうございます。
for i := 0; i < procs; i++ { | ||
file, err := os.Open(filepath.Join(dlTmp, strconv.FormatInt(int64(i+1), 10))) | ||
// 閉じるの失敗しても実害ないので、エラー処理なし | ||
defer file.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これは if err != nil {...}
の下にあったほうがよいです
err != nil
の場合、Openの実行にエラーがあったということなので、fileが開かれていないと考えたほうが良いです
まずエラーをチェックして、エラーがなければ、fileを触る、としたほうが安全です
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ありがとうございます。
ここは単純にミスです。。。
files[i] = file | ||
} | ||
reader := io.MultiReader(files...) | ||
b, err := ioutil.ReadAll(reader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ここも io.Copy
で良いと思います
レビュー指摘事項に対応。下記はまだ未対応 kadai3-2 |
課題3
タイピングゲームを作ろう
下記のような構成で実現した
main.go
オプションパラメタの読み込み、設定ファイルの読み込み、
gameからチャネルに送られてくる情報を整形して出力する責務
/typing
語彙を格納し、ゲームレベルに応じた語彙を返却する責務
語彙をレベル毎に、ゲーム性のある(ランダムな)値を返却する責務
ゲーム全体の制御と、進行状況のデータ保持を行う。主にやっていることは下記
分割ダウンロードを行う
golang.org/x/sync/errgourpパッケージなどを使ってみる
下記のような流れで実現した
すでに対象ファイルが存在していたら、DLせずスキップする