🙌
2次元フィールドをオブジェクトで表現する
Why
競プロによくある入力がH x W
であたえられるようなデータ構造を単純に2次元の配列として扱うと
範囲外アクセスのチェックなどが煩雑だなと感じたので、オブジェクトとしての表現を考えてみました。
実装
<<~NOTE
c c c W
r 0 1 2
r 3 4 5
r 6 7 8
H
NOTE
class Field
attr_reader :height, :width
def initialize(h, w, init_val = nil)
@height = h
@width = w
@field = Array.new(h * w, init_val)
end
def get(r, c)
return nil if r < 0 || c < 0
return nil if r >= height || c >= width
@field[pos(r, c)]
end
alias [] get
def get!(r, c)
raise if r < 0 || c < 0
raise if r >= height || c >= width
@field[pos(r, c)]
end
def set(r, c, val)
return false if r < 0 || c < 0
return false if r >= height || c >= width
@field[pos(r, c)] = val
true
end
def set!(r, c, val)
set(r, c, val) || raise
end
alias []= set!
def row(r)
@field[r*width, width]
end
def col(c)
arr = []
while (c < width * height)
arr << @field[c]
c += width
end
arr
end
def rows
Enumerator.new do |y|
height.times do |r|
y << row(r)
end
end
end
def cols
Enumerator.new do |y|
width.times do |c|
y << col(c)
end
end
end
def dump
@field.each_slice(width){|a| p a }
end
private
def pos(r, c)
r * width + c
end
end
example
H = 10
W = 4
field = Field.new(H, W, 0)
H.times do |r|
W.times do |c|
field.set(r, c, (r + c)**2)
end
end
field[3, 0] = 1000
puts field[3, 0]
field.dump
p field.row(0)
p field.row(3)
p field.col(0)
p field.col(1)
field.rows.each do |row|
puts row.sum
end
field.cols.each do |col|
puts col.sum
end
Discussion