同値関係を求めるパズル
元ネタもやっていなかったので、脳トレがわりにやってみた。
断っておきますが、やってみただけです。特に考察などを加えているわけではないので、あしからず。
今回のエントリは結城浩の『Perlクイズ』2004-06-18 No.0087をベースにしています。
問題
xxxx=yyyy という形式のデータをたくさん受け取り、等しいもの同士をグルーピングするプログラムを書いてください。データは標準入力から与え、グルーピングした結果は { xxxx, yyyy } のように集合のような形式で標準出力に出すことにします。以下に入力と出力の組の例を示します。グループ同士の出力順は問いませんが、グループの中の各要素は適宜ソートしてください。
同値関係を求めるパズル - rubyco(るびこ)の日記
自力で解いてみた。
m=[] ARGF.read.each_line{|line| next unless /(.*?)=(.*)/ =~ line.chomp m.push [$1, $2] } r = [] while m.size > 0 g={m[0][0] => true} while m.reject!{|x1, x2| g[x1] = g[x2] = true if g[x1] || g[x2]} end r.push g.keys end r.each{|g| puts "{ " + g.sort.join(", ") + " }" }
ビバ、力技。
特にwhile reject!あたりがナントモなコード…。
さて、最近の傾向のとおり、ここで出てくる変数を減らしていく。
ここで利用されるのは、ブロック内部での:x1, :x2, :lineと、最後にlocal_variablesをすると返ってくる :m, :g, :rの6つ。これを最終的には0にしたい。
マズは読み込み部分。
要はxxxxx=yyyyyになっている部分を抜き出せば良いわけで、それ以外は無視しているわけだけど、これは無駄。要するにソレはString#scanなわけで、ここはソレに置き換えすると:lineが減らせる。
次に出力部分。
一旦配列に収めているけど、これは冗長。
出力用内容の格納変数なぞは用意する必要も無く、その場で出力してしまえば話は早い。
これによって:rが無くなる。
最後に、reject!に対する引数。
ブロック引数を配列として一つの変数で受け取る。
で、こうしてみた。
m=ARGF.read.scan(/(.*?)=(.*)/) while m.size > 0 g={m[0][0] => true} while m.reject!{|x| g[x[0]] = g[x[1]] = true if g[x[0]] || g[x[1]]} end puts "{ " + g.keys.sort.join(", ") + " }" end p local_variables # => ["m", "g"]
で、大分短くなったし、変数も減らせた。
しかし、まだmとgとxの3つが残っている。
さて、ここから減らすにはどうしたらいいだろうか。