String#replace doesn't change the object the variable refers to, it
just modifies the contents of the string; the internal state of the
same object. This can't be done with integers and symbols, for
example, because they are immutable. And crossing types would be
impossible. So, you really don't want String#replace for everything
else, you want something new.
There have already been several good explanations of why variables
don't act this way in ruby, so I'll skip that. I'll just say while
ruby's method is diferent from any other language I've worked with, it
is not inconsistant, and once I figured out the model, I found other
ways to do things than via "pass by reference". I still end up wanting
to do things in the caller's scope (binding) occasionally, but that's
not the same issue.
That said, just about anything is possible in Ruby, so here you have
it: a Ref class.
class Ref
instance_methods.each{|m| undef_method(m)}
def self.new(obj)
ref = allocate
ref.become obj
ref
end
def method_missing(*args, &block)
@ref.__send__(*args, &block)
end
def become(obj)
old = @ref
@ref = obj
old
end
def __value__() @ref end
end
when you call Ref.new(obj), you get back a Ref object, which will
appear in every way to be the "obj" that you passed to Ref.new. It
will give the same id, behave the same, respond to the same methods,
etc.
Only two differences (that I see):
First, it responds to two extra methods: __value__ and become. become
replaces the object that is being referenced, and returns the old
value. __value__ returns the object being referenced, without the Ref
wrapper. You should use this method any time you want to serialize or
Marshal the value.
Second, it circumvents the public/private method checks, so you can
call private methods directly on the reference. Sorry. Don't have the
time to figure out how to fix that without eval.
Examples:
def do_something(a,b,c)
a.become a+1
b.become b+2
c.become c+3
end
==>nil
a,b,c = Ref.new(2), Ref.new(4), Ref.new(6)
==>[2, 4, 6]
do_something(a,b,c)
==>6
[a,b,c]
==>[3, 6, 9]
do_something(a,b,c)
==>9
[a,b,c]
==>[4, 8, 12]
a = Ref.new(12)
==>12
a.id
==>25
a.become a.to_f
==>12
a
==>12.0
a.id
==>2938980
a.become a.to_s
==>12.0
a
==>"12.0"
a.id
==>2927800
In closing, while this was a fun exercise to code, I doubt I'll ever
use it
It's the wrong hammer for my particular nails.
cheers,
Mark
···
On Thu, 20 Jan 2005 00:11:40 +0900, Michel Martens <blaumag@gmail.com> wrote:
It's easy to accomplish this task with strings:
def do_something(a, b, c)
a.replace((a.to_i + 1).to_s)
b.replace((b.to_i + 2).to_s)
c.replace((c.to_i + 3).to_s)
end
a, b, c = '5', '6', '7'
do_something(a, b, c)
It would be nice to have a replace method for every object, then we could write:
def do_something(a, b, c)
a.replace(a + 1)
b.replace(b + 2)
c.replace(c + 3)
end
a, b, c = 5, 6, 7
do_something(a, b, c)