演習問題回答例
演習問題1
属性として身長と体重を追加しよう。体重は秘密にしよう。
仕様を決める。
- initialize で height, weight を渡す
- 省略時は nil
class Person @@variables = [:@name, :@born, :@height, :@weight] def initialize name, born = nil, height = nil, weight = nil @name, @born, @height, @weight = name, born, height, weight end attr_accessor :height attr_writer :weight def hash @@variables.map{|var| instance_variable_get(var)}.hash end def eql? o @@variables.all? do |var| instance_variable_get(var).eql? o.instance_variable_get(var) end end end
属性が増えたので、それに合わせて hash, eql? も変更した。
演習問題2
BMI を計算するメソッドを追加しよう。
体重[kg]
身長[m]
仕様を決める。
- height は Float で、単位は cm
- weight は Float で、単位は kg
- @height, @weight は Float でなくても、あるていど動くようにする
- 計算できない場合は NaN を返す
class Person def bmi Float(@weight) / (Float(@height) / 100.0) ** 2 rescue Float::NAN end end
演習問題3
Person#<=> を書き直そう。
その妥当な仕様は?p0 = Person.new('matz') p1 = Person.new('Matz', Time.local(1965, 4, 14)) p0 <=> p1 # => ?
仕様を決める。
class Person def <=> o return nil unless o.kind_of? Person @@variables.each do |var| lhs = instance_variable_get(var) cmp = lhs <=> o.instance_variable_get(var) next if cmp == 0 return cmp unless cmp.nil? return -1 if lhs.nil? return 1 end 0 end end
演習問題4
クラス Person には、いくつかのバグがある。 それを見つけ出して修正しよう。
バグを探す。
- @born に strftime メソッドがないと age で例外が発生する
- 計算できなければ nil を返す
- to_s が age を使用しているので、以下同文
- age が nil なら、@name のみとする
class Person def age (Time.now.strftime('%Y%m%d').to_i - @born.strftime('%Y%m%d').to_i) / 1_00_00 rescue nil end def to_s if a = age "#{name}(#{a})" else name end end end
こればバグか?
matz = Person.new('matz') matz.freeze matz.name # => "matz" matz.name.upcase! matz.name # => "MATZ"
グループワークのときに質問してみたら、みんなこれは放置しているそうだ。
あえて対応するなら、
class Person def name @name.dup end end matz.name.upcase! # => "MATZ" matz.name # => "matz"
間違いがあれば、指摘くださると助かります。