Tom Cloyd
2/11/2009 10:37:00 AM
Robert Klemme wrote:
> 2009/2/11 Tom Cloyd <tomcloyd@comcast.net>:
>
>> Tonight I'm exploring the problem of tossing a value at a class instance and
>> getting a value back - having it behave like a method, in other words. I've
>> not looked at this before, and I don't have much confidence about what I'm
>> doing. I have a simple example from Thomas 3rd ed., and have modified it a
>> bit. It's not working as I'd expect - at all.
>>
>> The code:
>>
>> === begin code
>> # test.rb
>>
>> def main
>> test = Test.new puts 'bye' # <=========== line 6
>> end
>>
>> class Test
>>
>> def t1=(val)
>> @val = val
>> return 99
>> end
>>
>> def t2=(val)
>> @val = val * 2
>> return 99
>> end
>>
>> end
>>
>> %w(rubygems ruby-debug).each{ |lib| require lib }
>>
>> Debugger.start
>> debugger
>>
>> main
>>
>> === end code
>>
>> I start this, and the debugger stops it. I put a break point a line 6, and
>> continue.
>> With execution stopped, I enter at the command line:
>>
>> (rdb:1) p test.t2 = 2
>> 2
>>
>> I should have gotten 4. I only ever get back the same number I put in. Can
>> someone tell me what the problem is, and what I have to do to get test.t2 to
>> do this simple thing I'm asking of it?
>>
>
> There is no problem - other than probably that you do not yet know
> that = always returns the right hand side no matter what you do in a
> method. This is a safety feature to not allow your expectations to
> fool you when seeing =. You can, however, see that the method works
> as implemented:
>
> irb(main):001:0> o = Object.new
> => #<Object:0x7ff9c69c>
> irb(main):002:0> def o.foo=(x) p x; 123 end
> => nil
> irb(main):003:0> o.foo = 300
> 300
> => 300
> irb(main):004:0> o.send :foo=, 400
> 400
> => 123
>
> As you can see, method foo= actually returns 123 as I have told her
> but Ruby never let's you see it - unless you use #send.
>
>
>> My other question: the "return 99" business is from Thomas. He doesn't
>> explain it. I've been reading about "return" and I cannot account for why
>> it's there. It doesn't seem to do any thing. Can someone explain THAT.
>>
>
> In this case it's redundant because it's the last statement in the
> method anyway and a method will return the value of the last
> expression evaluated. In others it's not:
>
> irb(main):011:0> def find(enum,x)
> irb(main):012:1> enum.each {|e| p e; return e if x == e}
> irb(main):013:1> nil
> irb(main):014:1> end
> => nil
> irb(main):015:0> find %w{foo bar baz}, "bar"
> "foo"
> "bar"
> => "bar"
> irb(main):016:0> find %w{foo bar baz}, "gogo"
> "foo"
> "bar"
> "baz"
> => nil
> irb(main):017:0>
>
> Here I'm using "return" to terminate the method early.
>
>
>> Finally, is this general approach I'm taking the best or usual way to send
>> data to a class method and get some output? Until now, I thought I had to
>> read an accessor variable to get output back, but I'm thinking now that that
>> is probably NOT the best way.
>>
>
> It is certainly not for assignments. And in the more general case I
> believe there is a tendency to rather separate these things, i.e.
> setting, getting and performing work. But there is no law which
> prevents methods from returning something useful. In any case you
> should be aware that methods "leak" the value of the last expression
> which can have side effects that you do not intend (i.e. a caller
> stores the value and then later accidentally modifies internal state
> of your object). Having said that you should always care what your
> methods will return - either explicitly or implicitly.
>
>
>> What I'm wanting to do, say, create a file object which gives me more data
>> when I call upon it (it'll read a record and do some processing, then pass
>> back the results.
>>
>
> I do not know the nature of your processing but to me it seems
> superior design to separate processing and reading. Take CSV as an
> example: you can iterate the file but do not get back lines (as in the
> case of File) but you get complete records which were parsed from the
> file. You could do something similar, e.g.
>
> class TomsFile
> Record = Struct.new :name, :size, :age
>
> def initialize(io)
> @io = io
> end
>
> def self.open(file)
> File.open(file) do |io|
> tf = TomsFile.new(io)
> begin
> yield tf
> ensure
> tf.cleanup
> end
> end
> end
>
> def each
> @io.each do |line|
> # assuming fields are separated by #
> yield Record.new(*line.split(/#/))
> end
> end
> end
>
> def cleanup
> # NOP for now
> end
>
> TomsFile.open "foo.tf" do |tf|
> tf.each do |rec|
> printf "Found %-10s %4d\n", rec.name, rec.size
> end
> end
>
> You could then add another class, say TomsFileProcessor which gets a
> TomsFile and does the processing like
>
> class TomsFileProcessor
> attr_reader :count, :sum
>
> def initialize(tf)
> raise ArgumentError unless tf.is_a? TomsFile
> @tf = tf
> @count = @sum = 0
> end
>
> def process(record)
> @count += 1
> @sum += record.size
> # leakage accepted
> end
>
> def process_all
> @tf.each {|rec| process(rec)}
> self # rather not accidentally return @tf
> end
> end
>
> And then
>
> TomsFile.open "foo.tf" do |tf|
> tfc = TomsFileProcessor.new tf
> tf.process_all
> printf "Summary count=%4d sum=%4d\n", tf.count, tf.sum
> end
>
> (all this untested)
>
> Of course, you could also devise other schemes of interaction.
>
>
>> Normally I'd use a method, but I'm trying to become more
>> "classy" in my programming, hence this effort. I'm basically still
>> struggling to find a good reason to use a class at all. So far, it's proven
>> far too difficult to get something OUT of a class instance. I don't see the
>> point of the bother, yet. I'm hoping some enlightenment will arrive soon.
>>
>
> Hopefully my remarks were helpful...
>
> Kind regards
>
> robert
>
>
God help me. I had NO idea about this "#send" business. None. Everything
look different now.
As for your other remarks, I don't know how to thank you for the labor
of conveying your teaching to me in such detail. You gave me back a
great deal for my questions. I qill try to make good use of it. I
actually AM building a small hash based cyclic graph database with my
present coding efforts. It's going quite well, and I have immediate need
for it, so all this chatter isn't just programming theory for me. I'd be
getting work done with it right now if it were completely working.
Thanks again!
Tom
--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tom Cloyd, MS MA, LMHC - Private practice Psychotherapist
Bellingham, Washington, U.S.A: (360) 920-1226
<< tc@tomcloyd.com >> (email)
<< TomCloyd.com >> (website)
<< sleightmind.wordpress.com >> (mental health weblog)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~