文字列から2次元配列を作る
どう書く.orgの「島の数をカウントする」にトライしていて、以下のような文字列が与えられたとき、扱いやすく2次元配列に変換しようとしてたんだけどちょっとハマッた。
area_str = "□■■□ □□■□ □■□□ □■■□"
結論を先に書いておくと、次のようにするといけた。※ただし1.8.7以降でないと動きません。
area = [].tap{|ary| area_str.lines{|line| ary.push(line.strip.chars.to_a)}}
1.9からのtapメソッドを使ってみた♪これを使うことで1行で書ける。それまでは以下のように書く必要があった。
area = []
area_str.lines{|line| area.push(line.strip.chars.to_a)}
本題に戻って、ハマッたところはcharsメソッドがうまく働かなかったところ。配列の数を調べてみると8となっている。4が期待値なのに!原因は全角文字がうまく識別されておらず変に1バイトで分割されていること。原因は分かってもどうすればいいのかがよく分からなかった。。ドキュメントとかを読んでいたら分かった。プログラム冒頭に次のコードを入れることで解決。
$KCODE = 's' # SJISの意味
島の数をカウントする
解けたから投稿したんだけど、後から削除できないんだね。。他の人の答えを見ると自分のは最初から2次元配列にしてあるから反則な気がしたので、変えたかったんだけど。。何度も投稿するのも微妙なので、やめた。ということでここに最終解答を載せておく。
WHITE = "□" BLACK = "■" @area @mark def scan(area_str) @area = [].tap{|ary| area_str.lines{|line| ary.push(line.strip.chars.to_a)}} @mark = Array.new(@area.size).map{ Array.new(@area[0].size, false) } puts "白の島は#{loop(WHITE)}です。" puts "黒の島は#{loop(BLACK)}です。" end def loop(color) counter = 0 @area.each_index{|y| @area[y].each_index{|x| counter+=1 if marking(y, x, color)}} counter end def marking(y, x, color) return false unless y.between?(0, @area.size-1) && x.between?(0, @area[0].size-1) && @area[y][x] == color && !@mark[y][x] @mark[y][x] = true marking(y, x+1, color) marking(y+1, x, color) marking(y, x-1, color) marking(y-1, x, color) true end # プログラムスタート! $KCODE = "s" area1 = "□■■□ □□■□ □■□□ □■■□" scan(area1)