ソード・ワールド2.0、2.5のレーティング表部分の解析結果(rating_parsed.rb)の冗長な部分を簡潔にする。
現状
rating_parsed.rbでは、各インスタンス変数を nil で初期化しており、またそれらを正規化するメソッド(nil ならば 0 等の既定値に変換したり、範囲内に収めたりするメソッド)を用意している。
|
def initialize |
|
@critical = nil |
|
@kept_modify = nil |
|
@first_to = nil |
|
@first_modify = nil |
|
@greatest_fortune = false |
|
@rateup = nil |
|
end |
正規化メソッドの例:
|
# @return [Integer] |
|
def first_modify |
|
return @first_modify || 0 |
|
end |
以下に示すように、これらはうまく使われていない。
rating_parsed.rbの #to_s には、必要なパーツを文字列に追加する処理が書かれているが、必要性の判断は nil との比較ではなく、0 等の既定値との比較で行われている。また、条件判定と式展開で正規化メソッドが2回呼び出されているため、インスタンス変数と既定値の比較が最大で3回行われている。
|
def to_s() |
|
output = "KeyNo.#{@rate}" |
|
|
|
output += "c[#{critical}]" if critical < 13 |
|
output += "m[#{Format.modifier(first_modify)}]" if first_modify != 0 |
|
output += "m[#{first_to}]" if first_to != 0 |
|
output += "r[#{rateup}]" if rateup != 0 |
|
output += "gf" if @greatest_fortune |
|
output += "a[#{Format.modifier(kept_modify)}]" if kept_modify != 0 |
|
|
|
if @modifier != 0 |
|
output += Format.modifier(@modifier) |
|
end |
|
return output |
|
end |
レーティング表の処理はSwordWorld.rbに書かれているが、ここでも必要性の判断は nil との比較ではなく、0 等の既定値との比較で行われている。
例1:
|
first_to = command.first_to |
|
first_modify = command.first_modify |
|
|
|
loop do |
|
dice_raw, diceText = rollDice(command) |
|
dice = dice_raw |
|
|
|
if first_to != 0 |
|
dice = dice_raw = first_to |
|
first_to = 0 |
|
elsif first_modify != 0 |
|
dice += first_modify |
|
first_modify = 0 |
|
end |
例2:
|
# rate回数が1回で、修正値がない時には途中式と最終結果が一致するので、途中式を省略する |
|
if rateResults.size > 1 || command.modifier != 0 |
|
text = rateResults.join(',') + Format.modifier(command.modifier) |
|
if command.half |
|
text = "(#{text})/2" |
|
if command.modifier_after_half != 0 |
|
text += Format.modifier(command.modifier_after_half) |
|
end |
|
end |
|
sequence.push(text) |
|
elsif command.half |
|
text = "#{rateResults.first}/2" |
|
if command.modifier_after_half != 0 |
|
text += Format.modifier(command.modifier_after_half) |
|
end |
|
sequence.push(text) |
|
end |
以上の例のように、各インスタンス変数の nil は使われておらず、0 等の既定値を直接設定すればよくなっている。
おそらく、構文解析器の #parsed にある、値をまとめて設定する処理との整合性をとるために、このような形になったのではないだろうか。
|
def parsed(rate, modifier, option) |
|
RatingParsed.new.tap do |p| |
|
p.rate = rate |
|
p.critical = option[:critical]&.eval(@round_type) |
|
p.kept_modify = option[:kept_modify]&.eval(@round_type) |
|
p.first_to = option[:first_to] |
|
p.first_modify = option[:first_modify] |
|
p.rateup = option[:rateup]&.eval(@round_type) |
|
p.greatest_fortune = option.fetch(:greatest_fortune, false) |
|
p.semi_fixed_val = option[:semi_fixed_val] |
|
p.tmp_fixed_val = option[:tmp_fixed_val] |
|
p.modifier = modifier.eval(@round_type) |
|
p.modifier_after_half = option[:modifier_after_half]&.eval(@round_type) |
|
end |
|
end |
変更案
RatingParsedの各インスタンス変数を原則として 0 等の既定値で初期化するようにする。そうすれば、正規化メソッドは不要になり、直接インスタンス変数を参照すればよくなる。nil での初期化のままにし、SwordWorld.rb側も含めて処理を書き換えることも一案だが、範囲内に収める等の処理もあるため、数値で表現しておく方が問題が生じにくいと思われる。
class RatingParsed
def initialize
@critical = nil
@kept_modify = 0
@first_to = 0
@first_modify = 0
@greatest_fortune = false
@rateup = 0
end
# @return [Boolean]
def half
@modifier_after_half != 0
end
# @return [String]
def to_s()
sequence = ["KeyNo.#{@rate}"]
sequence << "c[#{@critical}]" if @critical < 13
sequence << "m[#{Format.modifier(@first_modify)}]" if @first_modify != 0
sequence << "m[#{@first_to}]" if @first_to != 0
sequence << "r[#{@rateup}]" if @rateup != 0
sequence << "gf" if @greatest_fortune
sequence << "a[#{Format.modifier(@kept_modify)}]" if @kept_modify != 0
sequence << Format.modifier(@modifier) if @modifier != 0
return sequence.join
end
end
RatingParsedに値を範囲内に収める正規化処理のメソッドを用意する。これは構文解析後に1回行えばよい。
# rating_parsed.rb
class RatingParsed
def normalize!
# クリティカル値を3以上に設定する
@critical = (@critical || (half ? 13 : 10)).clamp(3..)
# ...
self
end
end
# SwordWorld.rb
class SwordWorld < Base
def rating(string) # レーティング表
command = rating_parser.parse(string).normalize!
# ...
end
end
構文解析器では、#parsed において、オプションが設定されたかを if で判定するように変更する必要があるが、構文解析中の判定にも重複箇所が多くあるため、専用のBuilderを作るのがよいかもしれない。#515 の変更にも合わせる。
ソード・ワールド2.0、2.5のレーティング表部分の解析結果(rating_parsed.rb)の冗長な部分を簡潔にする。
現状
rating_parsed.rbでは、各インスタンス変数を
nilで初期化しており、またそれらを正規化するメソッド(nilならば0等の既定値に変換したり、範囲内に収めたりするメソッド)を用意している。BCDice/lib/bcdice/game_system/sword_world/rating_parsed.rb
Lines 34 to 41 in c833464
正規化メソッドの例:
BCDice/lib/bcdice/game_system/sword_world/rating_parsed.rb
Lines 55 to 58 in c833464
以下に示すように、これらはうまく使われていない。
rating_parsed.rbの
#to_sには、必要なパーツを文字列に追加する処理が書かれているが、必要性の判断はnilとの比較ではなく、0等の既定値との比較で行われている。また、条件判定と式展開で正規化メソッドが2回呼び出されているため、インスタンス変数と既定値の比較が最大で3回行われている。BCDice/lib/bcdice/game_system/sword_world/rating_parsed.rb
Lines 81 to 95 in c833464
レーティング表の処理はSwordWorld.rbに書かれているが、ここでも必要性の判断は
nilとの比較ではなく、0等の既定値との比較で行われている。例1:
BCDice/lib/bcdice/game_system/SwordWorld.rb
Lines 82 to 95 in c833464
例2:
BCDice/lib/bcdice/game_system/SwordWorld.rb
Lines 325 to 341 in c833464
以上の例のように、各インスタンス変数の
nilは使われておらず、0等の既定値を直接設定すればよくなっている。おそらく、構文解析器の
#parsedにある、値をまとめて設定する処理との整合性をとるために、このような形になったのではないだろうか。BCDice/lib/bcdice/game_system/sword_world/rating_parser.y
Lines 215 to 229 in ca56617
変更案
RatingParsedの各インスタンス変数を原則として
0等の既定値で初期化するようにする。そうすれば、正規化メソッドは不要になり、直接インスタンス変数を参照すればよくなる。nilでの初期化のままにし、SwordWorld.rb側も含めて処理を書き換えることも一案だが、範囲内に収める等の処理もあるため、数値で表現しておく方が問題が生じにくいと思われる。RatingParsedに値を範囲内に収める正規化処理のメソッドを用意する。これは構文解析後に1回行えばよい。
構文解析器では、
#parsedにおいて、オプションが設定されたかをifで判定するように変更する必要があるが、構文解析中の判定にも重複箇所が多くあるため、専用のBuilderを作るのがよいかもしれない。#515 の変更にも合わせる。