selfassignment and close

S

Simon Strandgaard

I would like to overload the '+=' operator. But it doesn't seems to be
possible? If it isn't possible, then how to use the '+' operator and at
the same time get the instance #closed correctly ?


server> ruby a.rb
open 42
open 43
close 42
close 43
-----------------------
open 42
open 43
close 43
server> expand -t2 a.rb
class Iterator
def initialize(value)
@value = value
puts "open #{@value}"
end
def close
puts "close #{@value}"
end
def +(n)
self.class.new(@value + n)
end
end

a = Iterator.new(42)
b = a + 1
a.close
b.close

puts "-----------------------"

a = Iterator.new(42)
a += 1 # Question: How to do selfassignment ?
a.close
# 42 never gets closed!
server>
 
H

Hal Fulton

Simon said:
a = Iterator.new(42)
a += 1 # Question: How to do selfassignment ?
a.close
# 42 never gets closed!

What's happening here is that += is dependent on +, yet it is still
an assignment.

a += 1 is equivalent to a=a+1; so as you can see, a new object is
created and assigned to a.

You could always implement methods like succ or add that would
change the value without changing the object's identity.

For an analogy with strings and arrays, these have a #replace
method that will change all the contents without changing the
object's identity.

Or an append can be done with << on either of these also.

But = or += (with a nontrivial expression) would create new objects:

x = "abc"
y = [1,2,3]
def x.foo
puts "The string #{self} has a singleton"
end
def y.foo
puts "The array #{self} also has a singleton"
end

x.foo # The string abc has a singleton
y.foo # The array 123 also...
x << "def"
y << [4,5,6]
x.foo # The string abcdef has a ...
y.foo # The array 123456 has...

x += "ghi"
y += [7,8,9]
x.foo # Error
y.foo # would also be an error

Does this clarify any?

Hal
 
S

Simon Strandgaard

What's happening here is that += is dependent on +, yet it is still
an assignment.

a += 1 is equivalent to a=a+1; so as you can see, a new object is
created and assigned to a.

I know that. What I don't know, are if there are any tricks when dealing
with '+=' ?

You could always implement methods like succ or add that would
change the value without changing the object's identity.

Yes I already have #next and #prev.

Does this clarify any?

Conclusion: providing a PLUS ('+') operator, can be dangerous because the
user can invoke '+=' and the instance will not get #closed correctly.
safer to undef '+'.
 
A

Ara.T.Howard

Date: Fri, 28 Nov 2003 20:24:58 +0100
From: Simon Strandgaard <[email protected]>
Newsgroups: comp.lang.ruby
Subject: selfassignment and close

I would like to overload the '+=' operator. But it doesn't seems to be
possible? If it isn't possible, then how to use the '+' operator and at
the same time get the instance #closed correctly ?


server> ruby a.rb
open 42
open 43
close 42
close 43
-----------------------
open 42
open 43
close 43
server> expand -t2 a.rb
class Iterator
def initialize(value)
@value = value
puts "open #{@value}"
end
def close
puts "close #{@value}"
end
def +(n)
self.class.new(@value + n)
end
end

a = Iterator.new(42)
b = a + 1
a.close
b.close

puts "-----------------------"

a = Iterator.new(42)
a += 1 # Question: How to do selfassignment ?
a.close
# 42 never gets closed!
server>


something like this:


class Iterator
class << self
def objs; @objs ||= []; end
def reap; objs.each{|obj| obj.close}; end
def new(*args,&block); objs << (obj = super); obj; end
at_exit { Iterator.reap }
end
def initialize(value)
@value = value
puts "open #{@value}"
end
def close
puts "close #{@value}" unless @closed
@closed = true
end
def +(n)
self.class.new(@value + n)
end
end

#a = Iterator.new(42)
#b = a + 1
#a.close
#b.close

puts "-----------------------"

a = Iterator.new(42)
a += 1 # Question: How to do selfassignment ?
a.close
# 42 never gets closed!


if you rely on ObjectSpace.define_finalizer you don't really know when the
call will be made, but perhaps something using it would be better... this
would cause an apparent 'leak' in long running programs. the general idea -
track objects within the class itself and make sure to free them - should be
workable for you though.

food for thought...

-a
--

ATTN: please update your address books with address below!

===============================================================================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
| STP :: http://www.ngdc.noaa.gov/stp/
| NGDC :: http://www.ngdc.noaa.gov/
| NESDIS :: http://www.nesdis.noaa.gov/
| NOAA :: http://www.noaa.gov/
| US DOC :: http://www.commerce.gov/
|
| The difference between art and science is that science is what we
| understand well enough to explain to a computer.
| Art is everything else.
| -- Donald Knuth, "Discover"
|
| /bin/sh -c 'for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done'
===============================================================================
 
S

Simon Strandgaard

I would like to overload the '+=' operator. But it doesn't seems to be
possible? If it isn't possible, then how to use the '+' operator and at
the same time get the instance #closed correctly ?
[snip code]

if you rely on ObjectSpace.define_finalizer you don't really know when the
call will be made, but perhaps something using it would be better... this
would cause an apparent 'leak' in long running programs. the general idea -
track objects within the class itself and make sure to free them - should be
workable for you though.

I had something similar, a WrapperIterator which kept track of all clones.
When the operation were completed, I could then sweep them.
But I don't consider it as a 'real' solution :)

It has to be solved _correct_, before im satisfied.

--
Simon Strandgaard



server> expand -t2 wrapper_close.rb
require 'iterator' # http://raa.ruby-lang.org/list.rhtml?name=iterator

class Scanner
def initialize(iterator)
@iterator = iterator
end
def execute
a = @iterator.clone
b = @iterator.clone
c = @iterator.clone
d = @iterator.clone
end
end

# keep track of how many instances we have
class CountingIterator < Iterator::Collection
@@count = 0
def clone
@@count += 1
super()
end
def self.count; @@count end
def close
@@count -= 1
super()
end
end

class WrapperIterator < Iterator::Base
def initialize(iterator, stack=nil)
@iterator = iterator
@stack = stack || []
end
def clone
i = @iterator.clone
@stack << i
WrapperIterator.new(@iterator, @stack)
end
def close
@stack.map{|i| i.close; nil}
end
end

ary = (0..19).to_a
iterator = CountingIterator.new(ary)
#wrap_iterator = iterator
wrap_iterator = WrapperIterator.new(iterator)
s = Scanner.new(wrap_iterator)
s.execute
p CountingIterator.count
wrap_iterator.close # ensure all iterators gets closed
p CountingIterator.count
server> ruby wrapper_close.rb
4
0
server>
 
R

Robert Klemme

Simon Strandgaard said:
I know that. What I don't know, are if there are any tricks when dealing
with '+=' ?



Yes I already have #next and #prev.



Conclusion: providing a PLUS ('+') operator, can be dangerous because the
user can invoke '+=' and the instance will not get #closed correctly.
safer to undef '+'.

IMHO "+" carries the wrong semantics for an iterator in Ruby: you don't
want a new object to be created, instead you want the iterator to change
its state.

If you want to provide numeric updates, why not just define succ like
this:

def succ(inc=1) ... end

Regards

robert
 
S

Simon Strandgaard

On Mon, 01 Dec 2003 08:57:26 +0100, Robert Klemme wrote:
[snip]
If you want to provide numeric updates, why not just define succ like
this:

def succ(inc=1) ... end


Its already defined like this:


# move <i>n</i> steps forward
def next(n=1)
n.times { self.next1 }
self
end

# move <i>n</i> steps backwards
def prev(n=1)
n.times { self.prev1 }
self
end


Thanks anyway :)
 
R

Robert Klemme

Simon Strandgaard said:
On Mon, 01 Dec 2003 08:57:26 +0100, Robert Klemme wrote:
[snip]
If you want to provide numeric updates, why not just define succ like
this:

def succ(inc=1) ... end


Its already defined like this:

Great! So you read my mind even before I could. :)
Thanks anyway :)

You're welcome! :)

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,141
Messages
2,570,815
Members
47,361
Latest member
RogerDuabe

Latest Threads

Top