On 21.04.2008 20:48, Michael W. Ryder wrote: > Robert Klemme wrote: >> On 20.04.2008 23:43, Michael W. Ryder wrote: >>> I am part way through implementing a Rational math class to further >>> my understanding of Ruby and had a couple of questions that I can't >>> find the answer to in Pickaxe. >>> The first question regards creating a new instance of the class. The >>> initialize method expects two integer values. While I have no >>> problem making sure that they are integers I am not sure what to do >>> if they are not. Do I return nil or some other result? >> >> The return value of #initialize is ignored - always. The proper way >> to handle this would be to raise an exception - presumably ArgumentError. > > My background in older languages is slowing me down. I thought that > there might be a way to return a value signifying an invalid entry > without having to worry about exception handling. Haven't got that far > in Ruby yet. If I remember this correctly I found exceptions a bit tricky when I first met them (or did they hit me?) but the concept is much cleaner than using return values. For example, with exceptions it's much simpler and cleaner to exit several levels of call stack. Also, since you can have inheritance hierarchies of exception classes you can nicely control which errors you catch on which level of your application. And you do not need complicated if then else or case constructions for it. >>> how to make it so that Ruby calls the right method when it sees x + y. >> >> You need to implement #coerce, #+, #-, #/, #*, #+@, #-@ - that is if >> you want to have full support of basic math. #coerce is a bit tricky >> but I am sure there are tutorials around - I just don't have a URL >> handy. But you can watch how it works: >> > > I have noticed people using things like #+ before. Is that the same as > just + when defining a method? Yes. It's just a conventional way to refer to instance methods. ri uses this as well, you can do "ri String#length". And #-@ and #+@ are unary operators. rb(main):001:0> class Foo rb(main):002:1> def -@;end rb(main):003:1> end > nil rb(main):004:0> -Foo.new > nil rb(main):005:0> +Foo.new oMethodError: undefined method `+@' for #<Foo:0x7ff91648> from (irb):5 from :0 rb(main):006:0> > I created a multiplication method using > 'def *(x)' and it seems to work fine. Hopefully I can use the coerce > method in the Roman numerals class in Pickaxe to create my coerce method. I don't have the book right here but I believe that's a good starting point. >> For comparisons you just need to implement #<=> and then include >> Comparable in your class. >> > This should just be subtracting the two numbers and comparing the > results so that should be easy. And not having to define the other > methods makes it easier. Absolutely. >> And for completeness reasons you should also implement #eql?, #== and >> #hash. >> > Shouldn't #== be inherited from Object? It is but Object's #==, #hash and #eql? consider only identical objects to be equivalent. But you want different objects to be equivalent so you have to define the equivalence relation - this is what you do by implementing #== and #eql?. You also need #hash to make instances of your class behave properly as Hash keys. > #eql? should be just a matter > of comparing the two numbers after "reducing" the fractions. I am not > sure on the #hash method though. Do you have any ideas where to start? That value should be derived from your reduced value. That way you ensure that whatever denominator and numerator are used to represent the same value they fall into the same bucket. And that's what you want because you want them to be equivalent. Typically it is derived from object member variable's #hash values via some combination of shift and XOR. Example: irb(main):001:0> class Foo irb(main):002:1> attr_accessor :x, :y irb(main):003:1> def hash irb(main):004:2> ((x.hash << 3) ^ y.hash) & 0xFFFF irb(main):005:2> end irb(main):006:1> end => nil irb(main):007:0> f=Foo.new => #<Foo:0x7ff82e2c> irb(main):008:0> f.hash => 36 irb(main):009:0> nil.hash => 4 irb(main):010:0> f.x=10 => 10 irb(main):011:0> f.hash => 172 irb(main):012:0> f.y=20 => 20 irb(main):013:0> f.hash => 129 irb(main):014:0> > Thank you for your assistance with this. I am learning a lot from this > exercise. You're welcome! I hope Ruby makes for a pleasant learning experience. If it feels rough from time to time, just tell yourself that other languages are a lot harder. :-) Kind regards robert