Hatena::Grouprubyist

Ruby初心者prinyの学習帳 RSSフィード

2009-01-14練習:たのしいRuby P.172 - 173

練習問題 (2)

16:25

摂氏を表すクラス Celsius を定義しましょう。
このクラスは、以下のメソッドを持つとします。
■Celsius.new(c)
 摂氏の温度 c の値を持つ Celsius クラスのインスタンスを作る。
■Celsius#to_celsius
 摂氏の温度を数値として返す。
■Celsius#to_fahr
 華氏の温度を数値として返す。
■Celsius#+(cl)
 摂氏オブジェクト cl と足し算をして、新しい摂氏オブジェクトを返す。

書いたもの

$KCODE = "SJIS"

class Celsius
  # 摂氏の温度 c の値を持つ Celsius クラスのインスタンスを作る。
  def initialize(c)
    @c = c
  end
  
  # 摂氏の温度を数値として返す。
  def to_celsius
    @c
  end
  
  # 華氏の温度を数値として返す。
  def to_fahr
    (@c * 9).quo(5) + 32
  end
  
  # cl が摂氏オブジェクトか数値オブジェクトだったら、足し算をして、
  # 新しい摂氏オブジェクトを返す。そうでなかったら、エラーを返す。
  def +(cl)
    case cl
    when Celsius
      Celsius.new(@c + cl.to_celsius)
    when Numeric
      Celsius.new(@c + cl)
    else
      $stderr.puts "in `+': Celsius can't be coerced into #{cl.class} (TypeError)"
    end
  end
end

cel1 = Celsius.new(37.0)
cel2 = Celsius.new(-2)
cel3 = Celsius.new(-17.7777777777778)
p "cel1: 摂氏 #{cel1.to_celsius} 度、華氏 #{cel1.to_fahr}"
p "cel2: 摂氏 #{cel2.to_celsius} 度、華氏 #{cel2.to_fahr}"
p "cel3: 摂氏 #{cel3.to_celsius} 度、華氏 #{cel3.to_fahr}"

# 摂氏オブジェクトと摂氏オブジェクトの足し算
cel4 = cel1 + cel2
p cel4.is_a?(Celsius)
p "摂氏 #{cel1.to_celsius} 度 + 摂氏 #{cel2.to_celsius} 度 = 摂氏 #{cel4.to_celsius}"

# 摂氏オブジェクトと数値オブジェクトの足し算
cel5 = cel1 + 3.5
p cel5.is_a?(Celsius)
p "摂氏 #{cel1.to_celsius} 度 + 摂氏 3.5 度 = 摂氏 #{cel5.to_celsius}"

# 摂氏オブジェクトと文字列オブジェクトの足し算
cel6 = cel1 + "絶対零度"

実行結果

"cel1: 摂氏 37.0 度、華氏 98.6 度"
"cel2: 摂氏 -2 度、華氏 28.4 度"
"cel3: 摂氏 -17.7777777777778 度、華氏 -4.2632564145606e-014 度"
true
"摂氏 37.0 度 + 摂氏 -2 度 = 摂氏 35.0 度"
true
"摂氏 37.0 度 + 摂氏 3.5 度 = 摂氏 40.5 度"
in `+': Celsius can't be coerced into String (TypeError)

ありゃ。cel3 が思っていた結果になっていない。練習問題 (1) の結果から、"摂氏 -17.7777777777778 度、華氏 0 度" になると思ったのに。

原因は、自分で丸めなくても、コマンドラインの制限?で計算結果が丸められていたからみたい。小数点以下を 1 桁増やして、cel3 = Celsius.new(-17.77777777777778) にしたら、"cel3: 摂氏 -17.7777777777778 度、華氏 0.0 度" になった。

別の方法で何とかならないかなと思い、ためしに、四捨五入して表示するようにしてみた。そうしたら、小数点以下第 3 位までなら「華氏 0.0 度」になったけど、小数点以下第 4 位までにすると「華氏 -0.0004 度」になった。

私にとっては温度は小数点以下第 2 位まであれば十分で、それなら、メソッドの定義はこのままで、小数点以下第 3 位を四捨五入して、小数点以下第 2 位までの結果を得るようにすればよさそう。

より誤差が少なくて、使い勝手もよいものにするには、どうすべきなんだろう。

摂氏 <=> 華氏 が成り立つといいなと思ったけど、Celsius#to_celsius も Celsius#to_fahr も数値を返すから、メソッドチェーンはできないし、摂氏から華氏に変換したものをまた摂氏に変換しなおすことってあまりなさそうだし、いつまでも悩むのも何だし、次の問題にいこうかな。そういえば、Celsius#+(cl) では、何となく、数値オブジェクトも足せたほうがいいかなと思って足せるようにしたけど、Celsius#to_celsius や Celsius#to_fahr では数値オブジェクトは扱えないから、いい仕様ではないのかも。・・・未消化だけど、先に進んでみよう。