Menu
CoddyTech

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

一个 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 中,使用针对值所属类的 case 补全辅助函数 describe(value)。对于整数,它返回 integer 5 (odd) 或 integer 4 (even);对于浮点数,它返回保留两位小数的 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 在线编译器