著作一覧 |
えらく梃子摺った。
public class FooSet { List<Param> params = new ArrayList<Param>(); public Param add(Foo f) { Param p = new Param(f); params.add(p); return p; } public Param add(Param p) { Param op = (Param)p.clone(); params.add(op); return op; } public Param findByName(String s) { for (Param p : params) { if (s.equals(p.getFoo().getName())) { return p; } } return null; } public class Param implements Cloneable { Foo foo; // Fooはイミュータブル。 Param(Foo f) { foo = f; } public Object clone() { return super.clone(); } public String bar() { return foo.bar(this); } public Foo getFoo() { return foo; } ... } } public class Foo { String name; public Foo(String initName) { name = initName; } public String getName() { return name; } public String bar(FooSet.Param p) { .... return barResult; } }というような感じ。FooSet.ParamはFooの入れ物兼Foo用のデータの提供/保持者、Fooはストラテジ。で、たまたま特殊なFooが必要となった。
public FooSet { public Param { ... public Param find(String key) { // 追加 return findByName(key); } } } public class SpecialFoo extends Foo { public SpecialFoo(String name) { super(name); } public String getData(FooSet.Param p) { StringBuilder sb = new StringBuilder(); FooSet.Param o = p.find("BAR"); if (o != null) { sb.append(o.bar(p)); } sb.append(super.bar(p)); return new String(sb); } }こんなのも作る。
public class FooSetTest extends TestCase { // 追加 public void specialFooTest() throws Exception { FooSet f = new FooSet(); f.add(new Foo("BAR")); f.add(new SpecialFoo("BAZ")); FooSet.Param p = f.findBy("BAZ"); assertEqulas("BARRRRBAZZZZ", p.getData()); } }
すべてはうまく行くかのように見える。でも、うまく行かない。
次のように修正。
public class FooSet { ... public Param add(Param p) { Param np = new Param(p); params.add(np); return np; } ... public class Param { ... Param(Param p) { // 追加 foo = p.foo; } // clone()は削除だ。 } }
数の悪戯だろうと思ったが、試してみた。
if ARGV.length != 1 puts 'usage: ruby montyhall.rb count' exit(1) end class Gamer def initialize() @win = 0 @door = [false, false, false] @door[rand(3)] = true end attr_reader :win end class NoMover < Gamer def play() @select = rand(3) @win += 1 if @door[@select] end end class Mover < Gamer def play() @selection = rand(3) open = next_number(@selection) if !@door[@selection] if @door[open] open = next_number(open) end end new_selection = next_number(@selection) if new_selection == open new_selection = next_number(new_selection) end @win += 1 if @door[new_selection] end private def next_number(n) (n + 1) % 3 end end def show(title, player) (1...ARGV[0].to_i).each do |x| player.play() end puts "#{title} won #{player.win} times." end show("no-mover", NoMover.new()) show("mover", Mover.new())
とりあえず100000回やりゃいいだろう。
c:\home\arton\test>ruby montyhall.rb 100000 no-mover won 33554 times. mover won 66698 times.
まじですか!
と、落ち着いて考えればMover#playにプログラムしてる通りじゃん。なんかごちゃごちゃ書いているが結局これってこういうことだな。
def play() @select = rand(3) @win += 1 if !@door[@select] end
ジェズイットを見習え |