Menu
Coddy logo textTech

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
true

1つの 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
challenge icon

チャレンジ

簡単

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)) }
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Rubyオンラインコンパイラ