caseとクラス
CoddyのRubyジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 35/57。
case は値のクラスに応じて分岐できます。when Integer はすべての整数に一致します。これは、case が === で比較するためです。また、Integer === 5 は、5 が Integer(is_a?)かどうかを確認します。
def describe(value)
case value
when Integer then "integer #{value}"
when Float then format("float %.2f", value)
when String then "text of #{value.size} chars"
when nil then "nothing"
else "other: #{value.class}"
end
end
[5, 2.5, "hi", nil, [1, 2]].each { |v| puts describe(v) }出力:
integer 5
float 2.50
text of 2 chars
nothing
other: Arrayクラスに対する === は is_a? に従うため、スーパークラスを指定した when はそのサブクラスにも一致します。分岐は、最も具体的なクラスから最も一般的なクラスの順に並べます:
class Animal
end
class Dog < Animal
end
def kind(x)
case x
when Dog then "a dog"
when Animal then "some animal"
else "not an animal"
end
end
puts kind(Dog.new)
puts kind(Animal.new)
puts kind("Rex")
puts Animal === Dog.new出力:
a dog
some animal
not an animal
true1つの when に複数のクラスを列挙でき、分岐では、異なる型が混在するネストした構造の合計を求めるなど、値を使った実際の処理を行えます。
def total(value)
case value
when Integer, Float then value
when String then value.to_f
when Array then value.sum { |v| total(v) }
when Hash then total(value.values)
else 0
end
end
puts total([1, "2.5", { a: 3, b: [4, nil] }])出力:
10.5クラスによる分岐は、数値、文字列、配列など、クラスに新しいメソッドを追加できない値や、外部からのデータに適しています。自分で作成したクラスでは、ポリモーフィズムを優先してください。その場合、新しいクラスには新しいメソッドが必要になるのであって、すべてのcaseに新しい分岐を追加する必要はありません。
# 独自のクラス: それぞれに答えさせる
shape.area
# 外部からの組み込み値: クラスで分岐する
case value
when Integer then ...
when String then ...
endチャレンジ
簡単functions.rb のヘルパー describe(value) を、値のクラスに対する case を使って完成させてください。戻り値は、integer 5 (odd) または integer 4 (even)、小数点以下2桁の float 2.50、text "hi" (2 chars)、list of 3: 1, a, 2.5(要素を to_s で連結したもの)、nil に対する nothing、それ以外のすべてに対する other Symbol です。テストに登場するその他の種類はシンボルだけです。
提供されたコードは、各入力行を値(整数、少数、[a,b] リスト、nil、:name シンボル、またはテキスト)に変換し、それぞれに対して describe の結果を出力します。
コードは functions.rb に記述してください。main.rb には提供された入出力コードが含まれており、編集できません。
自分で試してみよう
require_relative 'functions'
# 提供された入出力コード: そのままにしておく
input = []
while (line = gets)
input << line.chomp
end
def parse(text)
return Integer(text) if text.match?(/\A-?\d+\z/)
return Float(text) if text.match?(/\A-?\d+\.\d+\z/)
return nil if text == "nil"
return text[1..].to_sym if text.start_with?(":")
return text[1...-1].split(",").map { |t| parse(t) } if text.start_with?("[")
text
end
input.each { |line| puts describe(parse(line)) }
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rubyオンラインコンパイラ