A simple question regarding ruby method argument

S

Shin guey Wong

Hi,

I am new to ruby and am learning it now. Can anyone point me how to pass
argument to method by reference? I mean that the methods/function able
to change the value of the argument. I am running windows using win32ole
and 1 of the method need to pass in an argument and the argument will
modify by the win32ole methods. But I don't know how to pass argument by
reference.

Thanks in advance.
 
T

Tim Becker

Can anyone point me how to pass
argument to method by reference? I mean that the methods/function able
to change the value of the argument.

Values are always passed by reference:

irb(main):011:0> class Test
irb(main):012:1> attr_accessor :a
irb(main):013:1> end

irb(main):014:0> def modify x
irb(main):015:1> x.a="hello"
irb(main):016:1> end

irb(main):017:0> t = Test.new
irb(main):018:0> t.a="there"

irb(main):019:0> modify t
irb(main):020:0> t.a
=> "hello"


what doesn't work is passing in a parameter `x`, assigning a new value
to x within a function and expecting that assignment to be
'transfered' outside the function scope:

def modify x
x=2 # this doesn't work, because it modifies the reference and not
# the referenced value
end

tmp=1
modify tmp # doesn't work as you'd like, tmp is still 1
puts tmp
=> 1


If you're familiar with Java, it's more or less the same behaviour.
-Tim
 
S

Shin guey Wong

Tim said:
tmp=1
modify tmp # doesn't work as you'd like, tmp is still 1
puts tmp
=> 1

Hi Tim,

If I have a method which will need to modify the parameter passing to
it, how do I going to do that in ruby? In c we can pass in reference
using the & or c# using ref or out. But I am not sure Ruby have this
kind of keyword or not. The method I use is define by the com dll and I
calling it using win32ole.

Thanks.
 
T

Tim Becker

If I have a method which will need to modify the parameter passing to
it, how do I going to do that in ruby?

Maybe you could post the source of the method so we can figure out how
to get around the problems you're experiencing.
In c we can pass in reference

But if you pass in a pointer in C and assign a new value to the
pointer itself (instead of changing what is being pointed at) you
won't change the pointer variable outside of method scope either... In
c:

#include <stdio.h>

void test( int * x)
{
x=0; // modifies the reference, doesn't have the intended effect
}

void test2 (int * x)
{
*x=0; // modifies the referenced values, works.
}

int main (int argc, char * argv[]) {
int i=1;
printf("%d\n", i);
test (&i);
printf("%d\n", i);
test2 (&i);
printf("%d\n", i);
}

-Tim
 
D

David A. Black

Hi --

Hi,

I am new to ruby and am learning it now. Can anyone point me how to pass
argument to method by reference? I mean that the methods/function able
to change the value of the argument. I am running windows using win32ole
and 1 of the method need to pass in an argument and the argument will
modify by the win32ole methods. But I don't know how to pass argument by
reference.

You're (almost) always passing references around anyway. Ruby doesn't
have references to references; all references are exactly one degree
away from the object to which they refer.

When you do this:

def amend(s)
s << " there."
end

str = "Hi"
amend(str)

you've changed the original string object:

puts str # Hi there.

because str holds a reference to that object, and s in the method is a
copy of str (i.e., a copy of the reference, not the string).


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
J

John Dearden

I am trying to write a method that changes the value of two arguments.
Stripped to the barest essentials, something like this:

def munge(a)
a = a + 1
end

zot = 3
print zot, "\n" # this should (and does) print '3'
munge(zot)
print zot, "\n" # this should print '4'. We wouldn't be here now if it
did!

Now, if I try the amend method given by David A. Black, it works as
expected. But it's passing a string object, not an integer.

Surely there is a way to give a method _something_ so that it can return
values to the caller. Of course, if I only ever had a single value to
return, I could make it the return value, but that's just begging the
question.

Thanks in advance,
John
 
D

David A. Black

Hi --

I am trying to write a method that changes the value of two arguments.
Stripped to the barest essentials, something like this:

def munge(a)
a = a + 1
end

zot = 3
print zot, "\n" # this should (and does) print '3'
munge(zot)
print zot, "\n" # this should print '4'. We wouldn't be here now if it
did!

Now, if I try the amend method given by David A. Black, it works as
expected. But it's passing a string object, not an integer.

Surely there is a way to give a method _something_ so that it can return
values to the caller. Of course, if I only ever had a single value to
return, I could make it the return value, but that's just begging the
question.

Since integers are immutable (luckily!), and local variables are
local, you can't change an integer object via a reference copied to a
method. If it helps, I can assure you that you would *not* want this
to happen. It would be very weird if my local variables bindings were
at the mercy of methods that I called.

What you're really looking for is an object container, and local
variables don't really work that way. For container semantics you
really need to use containers. You could do something like:

def munge(a)
[*a][0] += 1
end

z = 3
y = *munge(z)

and so forth.


David

--
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242)
RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
 
J

John Dearden

David A. Black wrote:
...stuff deleted...
Since integers are immutable (luckily!), and local variables are
local, you can't change an integer object via a reference copied to a
method. If it helps, I can assure you that you would *not* want this
to happen. It would be very weird if my local variables bindings were
at the mercy of methods that I called.

What you're really looking for is an object container, and local
variables don't really work that way. For container semantics you
really need to use containers. You could do something like:

First, thanks for the quick answer. That was unexpected.

Unfortunately I can't say I understand the part of your response I
quoted above, other than that it says, "Nope, you can't do what you're
trying to do. Unless you're working with strings, which are somehow
different."

The "local variables _bindings_" bit implies to me this has something to
do with Ruby's underlying storage that I have not yet grasped. I'm
still thinking of a 'local variable' as really some chunk of memory, and
the way the bits are set in that memory mean something in the context of
my program. If I pass that location to a subprogram, and know that it's
going to put something else in it, that's good.

I can think of examples where I would like my subprogram to modify the
local variables of the calling code. For instance, I have two points,
and I want to calculate m and b for the formula of a line, as in y = m *
x + b. I would be inclined to write a subprogram calc_m_b(x1, y1, x2,
y2, var m, var b) (in Pascalese). I could do what amounts to the same
thing with two functions, one returning m and the other b, but that
seems sort of silly in this case. I could design an object that
represented a line in some fashion, and let it hold m and b, but that's
not always what you'd want, either.

Maybe I'm not thinking in terms of objects enough yet?

Thanks again,
John
 
D

dblack

Hi --

David A. Black wrote:
...stuff deleted...

First, thanks for the quick answer. That was unexpected.

Unfortunately I can't say I understand the part of your response I
quoted above, other than that it says, "Nope, you can't do what you're
trying to do. Unless you're working with strings, which are somehow
different."

The "local variables _bindings_" bit implies to me this has something to
do with Ruby's underlying storage that I have not yet grasped. I'm
still thinking of a 'local variable' as really some chunk of memory, and
the way the bits are set in that memory mean something in the context of
my program. If I pass that location to a subprogram, and know that it's
going to put something else in it, that's good.

I can think of examples where I would like my subprogram to modify the
local variables of the calling code. For instance, I have two points,
and I want to calculate m and b for the formula of a line, as in y = m *
x + b. I would be inclined to write a subprogram calc_m_b(x1, y1, x2,
y2, var m, var b) (in Pascalese). I could do what amounts to the same
thing with two functions, one returning m and the other b, but that
seems sort of silly in this case. I could design an object that
represented a line in some fashion, and let it hold m and b, but that's
not always what you'd want, either.

Maybe I'm not thinking in terms of objects enough yet?

I agree my answer was sort of "You can't do the thing you just said
you can't do" :) But that is sort of how it is. It's axiomatic in
Ruby that local variables are local. By "bindings" I mean:

def x(a)
a = 3 # a is bound to 3
end

a = 1 # *this* (different) a is bound to 1
x(a)
puts a # still 1

The call to x does not change the existing binding of a.

The only kind of change you can do in a situation like this is when a
variable is bound to a mutable object -- or, more accurately, a
variable contains a reference to a mutable object (since basically
Ruby variables traffic in references). That's where you get into
things like:

def add_to_array(a)
a.push("New element")
end

array = [1,2,3]
add_to_array(array)
p array # [1,2,3,"New element"]

If I use 'a' instead of 'array' for my array, the fact that
add_to_array also uses 'a' is just coincidence. There's no connection
between the two. It just happens that the identifier 'a' is used in
two local scopes to handle references to the same object.

The usual patterns you see in Ruby are either container-based,
object-based (where you have an object with an attribute; you pass the
object into the method and it does something to the attribute), or
assignment-based but in the same scope:


def change_me(a)
a + 2
end

a = 1
a = change_me(a) # a is now 3

I've pushed the assignment out of the method -- where it doesn't have
any effect except to set or change the method's local variables --
into the calling context. That way, I can do whatever I want with my
identifiers in each scope.


David

--
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242)
RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
 
L

Logan Capaldo

I am trying to write a method that changes the value of two arguments.
Stripped to the barest essentials, something like this:

def munge(a)
a = a + 1
end

zot = 3
print zot, "\n" # this should (and does) print '3'
munge(zot)
print zot, "\n" # this should print '4'. We wouldn't be here now if it
did!

Now, if I try the amend method given by David A. Black, it works as
expected. But it's passing a string object, not an integer.

Surely there is a way to give a method _something_ so that it can return
values to the caller. Of course, if I only ever had a single value to
return, I could make it the return value, but that's just begging the
question.
or you can return more than one value

def munge(a, b)
return a + 1, b - 2
end

q = 4
r = 9

q, r = munge(q, r)
 
J

John Dearden

Logan said:
or you can return more than one value

def munge(a, b)
return a + 1, b - 2
end

q = 4
r = 9

q, r = munge(q, r)

Ah, well, now we're on to something! It looks a bit clunky to my
untrained eye, but it will definitely do the job, and honors the Ruby
'local variables are local' way.

Thanks!
John
 
L

Logan Capaldo

Ah, well, now we're on to something! It looks a bit clunky to my
untrained eye, but it will definitely do the job, and honors the Ruby
'local variables are local' way.
You're right it does look a little clunky, but I doubt your problem is
so mundane in reality. (And I suspect ultimately there's a better
solution than out / ref vars or multiple returns for your actual
problem. But since we don't know what that is, we can't help :) )
 
D

dtuttle1

Hi Tim - your example is great but I don't you phrased that right.

You're saying:
Ruby passes objects by reference

I think it's:
Ruby passes object references by copy

Same as Java, like you said.

--Dave
 
D

dblack

Hi --

Ah, well, now we're on to something! It looks a bit clunky to my
untrained eye, but it will definitely do the job, and honors the Ruby
'local variables are local' way.

Don't worry; Ruby won't let you dishonor that :)

Keep in mind that most of the time, you'll probably be assigning the
results to other variables. Using my earlier example:

def change_me(a)
a + 2
end

you'd then probably do something like:

n = 2
m = change_me(n) # as opposed to reassigning to n

which looks a little less clunky and, in a real program, would
probably make lots of sense. Both I and Logan (I surmise) provided you
with examples that did the reassigning to the same variable, just to
match what you were doing as closely as possible, but it's a general
technique.

Of course, you'll also see a lot of this:

n = m.change_me

depending on exactly what's going on.


David

--
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242)
RUBY FOR RAILS (http://www.manning.com/black)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
 

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
473,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top