Arrayにpick_oneメソッドを追加

数独の問題を生成するプログラムを習作として書いてみようとして、てこずっている。素朴なアプローチですら、それをサラサラと書き下せるほど基本的なArrayクラスの扱いも分かっていないからだと思う。

「えっと、多重配列ってどうやって初期化するんだっけ。いや、シンプルに1次元配列を作って、それを後からx、yに換算するか。えーと、3x3のブロックの配列を戻すメソッドの定義は……、あっ、mapを使えばシンプルにかけるぞ、いや、違う違う、いやそうそう、うんできた。で、オレは@matrixって1次元配列にしたんだっけかな? あれ、なんでそんなことにしたんだっけか?」

とかなんとか。

ともあれ、数独を扱っていて、次のような課題が出てきた(というか出てきそうな気がしている)。配列からランダムに1つ要素を返すメソッドがほしい。標準であっても良さそうだけど、ないみたい。

[1,2,3,4,5,6,7,8,9]という配列を破壊的に操作して、ランダムに1つずつピックアップし、徐々に減らしていくことで、横1列を埋めるような操作を考えている(数独では縦、横には1度しか同じ数字が登場しない)。

ary[rand(ary.size)]とやればいいわけだけど、毎回毎回書くのもバカみたいだし、変数名が長くなると表現が冗長。こういうとき、この関数ってどこで定義すればいいんだろうか。

moduleにする? 違うよなぁ。トップレベルで関数的メソッドにすればいいのかとも思ったけど、なんか違う。Matrixクラスに入れるのも場所が違う。と考えて、そっか、こういうときはArrayにメソッドを足してしまえばいいんだと思った。それが当たり前のようにピッタリの場所だし、使うときにも自然。

class Array
  def pick_one
    return self[rand(self.size)]
  end
end
irb(main):003:0> a = (0..9).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):004:0> a.pick_one
=> 9
irb(main):005:0> a.pick_one
=> 7
irb(main):006:0> a.pick_one
=> 4
irb(main):007:0> 100.times {print a.pick_one}
6890757404105794966603981198754834370786577577713102644567157695020757
354108157988510173596323687415=> 100
irb(main):008:0> 100.times {print "#{(a - [1,3,5,7,9]).pick_one}"}
8448208244426604086462604064060288420042024066244428820880482848800848
040688686224262868462602268044=> 100

これがオープンクラスの威力? こういうことをやっていのかどうか良く分からない。デメリットって何だろうか。ということを考え始めると、もっと他人が書いたコードを見ないといけないんだろうなぁと思った。

あ、破壊的操作といいながら、配列を破壊するのを忘れていた。こうかな?

class Array
  def pick_one!
    r = rand(self.size)
    e = self[r]
    self.delete_at(r)
    return e
  end
end
irb(main):002:0> a = (0..9).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):003:0> a.pick_one!
=> 5
irb(main):004:0> a
=> [0, 1, 2, 3, 4, 6, 7, 8, 9]
irb(main):005:0> a.pick_one!
=> 3
irb(main):006:0> a
=> [0, 1, 2, 4, 6, 7, 8, 9]

動いてるらしい。