プログラミング言語/Ruby/コードリーディング/しりとりゲーム/GameMasterクラス
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
*GameMasterクラス [#dab7af55]
その名の通り、しりとりゲームを管理するクラス。
まずはinitialize()メソッドを見てみよう。説明しておくと、
クラスからnewされたインスタンスは、このメソッドによって初...
**リスト1-1 GameMaster#initialize [#tf0c1cd6]
def initialize
@dictionary = []
@players = []
@answer_log = [] #assoc list ... [word, player_id]
@before = nil
end
`@'というプレフィクス付きの変数がインスタンス変数だ。
空のブランケット(`[]')は長さゼロの配列を生成するので、
@dictionary, @players, @answer_logは配列である。
@answer_log脇にコメントがあるが、これは@answer_logを、
0 | "vip" | "わたし"
1 | "poison" | "あなた"
2 | "noodle" | "わたし"
3 | "enjoy" | "あなた"
.
.
.
といった形式で保存しておくということだ。
配列の入れ子による二次元配列(もどき)である。
宣言されていない(=まだ一度も代入されていない)インスタンス...
参照するとnilであるが、
「始めはnilである値だ」ということを明示するために@beforeに
nilを代入している。
#br
次に、インタフェース(メソッド)を見てみる。
だいたい呼ばれる順に見ていこう。
**リスト1-2 GameMaster#dictionary_load [#de212032]
def dictionary_load(f)
f.each do |line|
line.strip!
@dictionary.push line.downcase if /\A\w+\Z/ =~ line
end
end
名前まんまのメソッド。引数のfはIOオブジェクトを期待してい...
毎行読んで、前後の空白や改行をとっぱらって、単語らしき文...
@dictionaryに追加していく。その際、紛らわしさをなくすため...
によって小文字のみにしている。
**リスト1-3 GameMaster#entry [#p9ec995c]
def entry(pl)
pl.dictionary = @dictionary
pl.game_master = self
@players.push pl
end
plはPlayerクラスを期待。といっても、実際はManualPlayerかA...
Playerのdictionaryとgame_masterになにやら代入している。し...
今までの解答の記録を調べたりする必要があるので、自らのGam...
しりとり勝負するPlayerが別々の辞書を使っていても意味がな...
**リスト1-4 GameMaster#each [#q9891230]
def each
loop do
break unless (ret = succ)
yield ret
end
end
イテレータの定義。yieldはブロック付きメソッド呼び出しで渡...
実行する。
GameMasterにおける「繰り返し」の「次に進む」という定義は...
メソッドに預けている。
**リスト1-5 GameMaster#succ [#ce45a742]
def succ
turn_player do |pl|
wd = pl.answer(@before)
if wd
@answer_log.push [wd, pl.id]
@before = wd
end
end
end
succは`succeed'の意だ。turn_playerというprivateメソッドの...
内でごにょごにょ処理している。先にこっちを見てみよう。
**リスト1-6 GameMaster#turn_player [#kb2aa8a8]
def turn_player
ret = nil
@pl = @players.shift
ret = yield(@pl)
@players.push @pl
ret
end
entryで@playersにはPlayerが詰められている。
Array#shiftは配列の先頭要素を取り出すメソッド、
Array#pushは配列の最後尾に要素を付け足すメソッドなので、
これは配列をキュー的に使ってることになる。
&ref(shiritori02.png);
entryした順にPlayerが入っているので、その順から先行後攻を...
呼び出す側からはPlayerの順番を意識せず使えるようになって...
なお、キューから引き出したPlayerを@plとして記録しているこ...
**リスト1-5 GameMaster#succ(再掲) [#jb016743]
def succ
turn_player do |pl|
wd = pl.answer(@before)
if wd
@answer_log.push [wd, pl.id]
@before = wd
end
end
end
さて、turn_playerによりplにはその時の回答者であるPlayerが...
pl.answer(@before)
は、Playerに前の語を伝えて、回答を要求する、と読み解ける...
見るが、answerは「回答不能」の場合、nilを返す。よってその...
nilとなる(Rubyではifなども値を持ち、それは評価した節の最...
今回、else節が省略されているのでnilとなる)。
めでたく回答できた場合は、その答えと回答者のidを@answer_l...
また、その答えを@beforeとして記憶しておく。
**リスト1-7 GameMaster#now [#r318ef15]
def now
@pl
end
なんつーか、無茶苦茶簡素なメソッド。
前述したとおり、@plにはturn_playerが回答するPlayerを入れ...
succやeach中で呼ぶと「現在の回答者」、そこ以外から呼ぶと...
を参照できる。
例えばeachが終了した直後のnowは敗者ということに。
**リスト1-8 GameMaster#twice? [#pb574578]
def twice?(word)
@answer_log.assoc(word) ? true : false
end
あとは@answer_logに関わる物ばかり。
ここで使用されているArray#assocはマイナーなメソッドである...
ary = [[key_1, value_1], [key_2, value_2], ... , [key_n,...
こういう入れ子した配列から、ary.assoc(key)でkey == key_x...
検索する。見つからない場合nil。
@answer_logは[(単語), (回答者)]という配列の配列なので、
wordが既に回答された単語かどうかの真偽を返す。
**リスト1-9 GameMaster#log_size [#m8ada088]
def log_size
@answer_log.size
end
これは単純に@answer_logの要素数を返す。使用された単語の数...
するから実装。
**リスト1-10 GameMaster#log_search [#mb67024c]
def log_search(word) #[nth, id]
ret = nil
@answer_log.each_with_index do |x, i|
if x[0] == word
ret = [i+1, x[1]]
break
end
end
ret
end
twiceの類似品みたいなものだが、分けて別に実装。
Array#each_with_indexは名前の通り、要素のみならずインデッ...
繰り返すイテレータ。それを利用して単語から[(回答された順...
割り出す。
ここで注意してほしいのが、まずretにnilを代入しているとこ...
wordが検索に引っかからなかった場合に偽を返すという点でも...
それ以前にRubyのブロックローカルスコープに対処するためで...
メソッドのローカルスコープはブロック内から見えるが、ブロ...
(つまり初めての代入)された変数はそのブロック内だけで消え...
かといってブロック外のretと内のretが別物かというと、そう...
この辺りのスコープの問題はRubyがよく突っつかれる部分の一...
挙動は変わるかもしれないし、変わらないかもしれない。
終了行:
*GameMasterクラス [#dab7af55]
その名の通り、しりとりゲームを管理するクラス。
まずはinitialize()メソッドを見てみよう。説明しておくと、
クラスからnewされたインスタンスは、このメソッドによって初...
**リスト1-1 GameMaster#initialize [#tf0c1cd6]
def initialize
@dictionary = []
@players = []
@answer_log = [] #assoc list ... [word, player_id]
@before = nil
end
`@'というプレフィクス付きの変数がインスタンス変数だ。
空のブランケット(`[]')は長さゼロの配列を生成するので、
@dictionary, @players, @answer_logは配列である。
@answer_log脇にコメントがあるが、これは@answer_logを、
0 | "vip" | "わたし"
1 | "poison" | "あなた"
2 | "noodle" | "わたし"
3 | "enjoy" | "あなた"
.
.
.
といった形式で保存しておくということだ。
配列の入れ子による二次元配列(もどき)である。
宣言されていない(=まだ一度も代入されていない)インスタンス...
参照するとnilであるが、
「始めはnilである値だ」ということを明示するために@beforeに
nilを代入している。
#br
次に、インタフェース(メソッド)を見てみる。
だいたい呼ばれる順に見ていこう。
**リスト1-2 GameMaster#dictionary_load [#de212032]
def dictionary_load(f)
f.each do |line|
line.strip!
@dictionary.push line.downcase if /\A\w+\Z/ =~ line
end
end
名前まんまのメソッド。引数のfはIOオブジェクトを期待してい...
毎行読んで、前後の空白や改行をとっぱらって、単語らしき文...
@dictionaryに追加していく。その際、紛らわしさをなくすため...
によって小文字のみにしている。
**リスト1-3 GameMaster#entry [#p9ec995c]
def entry(pl)
pl.dictionary = @dictionary
pl.game_master = self
@players.push pl
end
plはPlayerクラスを期待。といっても、実際はManualPlayerかA...
Playerのdictionaryとgame_masterになにやら代入している。し...
今までの解答の記録を調べたりする必要があるので、自らのGam...
しりとり勝負するPlayerが別々の辞書を使っていても意味がな...
**リスト1-4 GameMaster#each [#q9891230]
def each
loop do
break unless (ret = succ)
yield ret
end
end
イテレータの定義。yieldはブロック付きメソッド呼び出しで渡...
実行する。
GameMasterにおける「繰り返し」の「次に進む」という定義は...
メソッドに預けている。
**リスト1-5 GameMaster#succ [#ce45a742]
def succ
turn_player do |pl|
wd = pl.answer(@before)
if wd
@answer_log.push [wd, pl.id]
@before = wd
end
end
end
succは`succeed'の意だ。turn_playerというprivateメソッドの...
内でごにょごにょ処理している。先にこっちを見てみよう。
**リスト1-6 GameMaster#turn_player [#kb2aa8a8]
def turn_player
ret = nil
@pl = @players.shift
ret = yield(@pl)
@players.push @pl
ret
end
entryで@playersにはPlayerが詰められている。
Array#shiftは配列の先頭要素を取り出すメソッド、
Array#pushは配列の最後尾に要素を付け足すメソッドなので、
これは配列をキュー的に使ってることになる。
&ref(shiritori02.png);
entryした順にPlayerが入っているので、その順から先行後攻を...
呼び出す側からはPlayerの順番を意識せず使えるようになって...
なお、キューから引き出したPlayerを@plとして記録しているこ...
**リスト1-5 GameMaster#succ(再掲) [#jb016743]
def succ
turn_player do |pl|
wd = pl.answer(@before)
if wd
@answer_log.push [wd, pl.id]
@before = wd
end
end
end
さて、turn_playerによりplにはその時の回答者であるPlayerが...
pl.answer(@before)
は、Playerに前の語を伝えて、回答を要求する、と読み解ける...
見るが、answerは「回答不能」の場合、nilを返す。よってその...
nilとなる(Rubyではifなども値を持ち、それは評価した節の最...
今回、else節が省略されているのでnilとなる)。
めでたく回答できた場合は、その答えと回答者のidを@answer_l...
また、その答えを@beforeとして記憶しておく。
**リスト1-7 GameMaster#now [#r318ef15]
def now
@pl
end
なんつーか、無茶苦茶簡素なメソッド。
前述したとおり、@plにはturn_playerが回答するPlayerを入れ...
succやeach中で呼ぶと「現在の回答者」、そこ以外から呼ぶと...
を参照できる。
例えばeachが終了した直後のnowは敗者ということに。
**リスト1-8 GameMaster#twice? [#pb574578]
def twice?(word)
@answer_log.assoc(word) ? true : false
end
あとは@answer_logに関わる物ばかり。
ここで使用されているArray#assocはマイナーなメソッドである...
ary = [[key_1, value_1], [key_2, value_2], ... , [key_n,...
こういう入れ子した配列から、ary.assoc(key)でkey == key_x...
検索する。見つからない場合nil。
@answer_logは[(単語), (回答者)]という配列の配列なので、
wordが既に回答された単語かどうかの真偽を返す。
**リスト1-9 GameMaster#log_size [#m8ada088]
def log_size
@answer_log.size
end
これは単純に@answer_logの要素数を返す。使用された単語の数...
するから実装。
**リスト1-10 GameMaster#log_search [#mb67024c]
def log_search(word) #[nth, id]
ret = nil
@answer_log.each_with_index do |x, i|
if x[0] == word
ret = [i+1, x[1]]
break
end
end
ret
end
twiceの類似品みたいなものだが、分けて別に実装。
Array#each_with_indexは名前の通り、要素のみならずインデッ...
繰り返すイテレータ。それを利用して単語から[(回答された順...
割り出す。
ここで注意してほしいのが、まずretにnilを代入しているとこ...
wordが検索に引っかからなかった場合に偽を返すという点でも...
それ以前にRubyのブロックローカルスコープに対処するためで...
メソッドのローカルスコープはブロック内から見えるが、ブロ...
(つまり初めての代入)された変数はそのブロック内だけで消え...
かといってブロック外のretと内のretが別物かというと、そう...
この辺りのスコープの問題はRubyがよく突っつかれる部分の一...
挙動は変わるかもしれないし、変わらないかもしれない。
ページ名: