インデックス付けと追加
CoddyのRubyジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 39/57。
obj[key] はメソッド [] を呼び出し、obj[key] = value は []= を呼び出します。これらを定義すると、クラスに配列やハッシュの角括弧構文を持たせ、クラスに適した任意の意味を与えられます。
class Inventory
def initialize
@stock = Hash.new(0)
end
def [](name)
@stock[name]
end
def []=(name, qty)
@stock[name] = qty
end
end
inv = Inventory.new
inv["apple"] = 5
inv["apple"] += 2
puts inv["apple"]
puts inv["pear"]出力:
7
0[] は、たとえば行や列など、複数の引数を受け取り、それらをチェックできます。存在しない位置に対して通常発生するエラーは IndexError です。
class Board
def initialize(size)
@size = size
@cells = Array.new(size) { Array.new(size, ".") }
end
def [](row, col)
check(row, col)
@cells[row][col]
end
def []=(row, col, mark)
check(row, col)
@cells[row][col] = mark
end
def check(row, col)
raise IndexError, "no cell #{row},#{col}" unless row.between?(0, @size - 1) && col.between?(0, @size - 1)
end
end
b = Board.new(3)
b[1, 1] = "X"
puts b[1, 1] + b[0, 0]
begin
b[3, 0]
rescue IndexError => e
puts e.message
end出力:
X.
no cell 3,0<< は追加演算子です。そこから self を返すことで、配列に対する list << a << b のような連結が可能になります。
class Shelf
def initialize
@books = []
end
def <<(title)
@books << title
self
end
def to_s
@books.join(" | ")
end
end
shelf = Shelf.new
shelf << "Dune" << "Emma" << "Ulysses"
puts shelf出力:
Dune | Emma | Ulysses[]= を通じた代入は、他のセッターと同様に、メソッドが返す値ではなく、代入された値を返します。角括弧と << によって、クラスに小さく親しみやすいインターフェースを持たせられます。
inv["apple"] = 5 # []=("apple", 5) を呼び出し、式は 5
inv["apple"] # []("apple") を呼び出す
shelf << "Dune" << "Emma" # << は self を返すので、チェーンできる
board[1, 2] = "X" # []=(1, 2, "X")チャレンジ
簡単クラス Inventory を完成させてください。inv[name] はアイテムの数量(未知のアイテムの場合は 0)を返し、inv[name] = qty は数量を設定し、inv << name はそのアイテムを 1 つ追加して、呼び出しをチェーンできるようインベントリを返します。to_s は、名前のアルファベット順で apple: 3, pear: 1 を返します。数量が 0 のアイテムは除外され、該当するアイテムがない場合は empty を返します。
提供されているコードはコマンドを読み取ります。set apple 5、add apple pear pear(すべての名前に対して << をチェーンします)、get apple、print を受け付け、get と print の結果を出力します。
コードは inventory.rb に記述してください。main.rb には提供されている入出力コードが含まれており、編集できません。
自分で試してみよう
require_relative 'inventory'
# 提供された入出力コード: そのままにしておく
input = []
while (line = gets)
input << line.chomp
end
inv = Inventory.new
input.each do |line|
cmd, *args = line.split
case cmd
when "set" then inv[args[0]] = args[1].to_i
when "add" then args.reduce(inv) { |target, name| target << name }
when "get" then puts inv[args[0]]
else puts inv
end
end
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rubyオンラインコンパイラ