Method arguments

Hello,

Sorry for asking this basic question, but I couldn't find it in the
PickAxe book.

When you pass an argument to a method, is it passed by reference, or by
value?
Or, does it depend on the object's type?

For example, in the simple script below, the program outputs "0".

···

#-----------------------------------
def test(c_count)
c_count = c_count + 1
end
# begin program
c_count = 0
test(c_count)
puts c_count
#-----------------------------------
I realize that for simple things, it's better to return a value from
the method, but I would like to know *why* it's not modifying my
"c_count" value.

I'm using ruby 1.8.2 (2004-11-06) [i386-mswin32]
Thanks in advance,

--Nate

This might get some disagreement since some people prefer to distinguish
'values' from 'objects' ...

Everything you manipulate in Ruby is an object.

All variables, method arguments, return values, instance variables, etc.
contain references to these objects. All methods are invoked on an object
via some such reference.

So
    x = y
makes the variable x refer to the same object that the variable y referred
to.

And
    x = a.foo(b)
makes the variable x refer to the object that is returned (as a reference)
by a.foo() when given a parameter that refers to the same object as that
refeerred to by variable b.

When you write
    c_count = 0
your variable c_count _refers_ to the integer object '0'. (Some 'built-in'
objects like integers exist magically, you never explicitly create them; you
just handle references to them).

When you write
    c_count + 1
you are sending the message :+ to the object referred to by c_count i.e.
'0'. This returns a _reference_ to another object, the object '1'.

c_count = c_count + 1
just updates the (parameter) variable c_count so it now refers to object
'1'.

Your external local variable still refers to the object '0'.

Cheers.

"Tookelso" <tookskie-googlegroups@yahoo.com> wrote in message
news:1104823101.584106.327050@z14g2000cwz.googlegroups.com...

···

Hello,

Sorry for asking this basic question, but I couldn't find it in the
PickAxe book.

When you pass an argument to a method, is it passed by reference, or by
value?
Or, does it depend on the object's type?

For example, in the simple script below, the program outputs "0".
#-----------------------------------
def test(c_count)
c_count = c_count + 1
end
# begin program
c_count = 0
test(c_count)
puts c_count
#-----------------------------------
I realize that for simple things, it's better to return a value from
the method, but I would like to know *why* it's not modifying my
"c_count" value.

I'm using ruby 1.8.2 (2004-11-06) [i386-mswin32]
Thanks in advance,

--Nate

When you pass an argument to a method, is it passed by reference, or by
value?

Passed by reference to an object.

For example, in the simple script below, the program outputs "0".
#-----------------------------------
def test(c_count)
c_count = c_count + 1

This creates a new object, gives it the value c_count+1 and has the
c_count variable point to it.

Try the following:

def test1(str)
  str = str.upcase
end

def test2(str)
  str.upcase!
end

a = "hello"
b = "world"
puts test1(a) #=> HELLO
puts test2(b) #=> WORLD
puts a #=> hello
puts b #=> WORLD

In test1, the variable is pointed to a new object. In test2, the object
to which the variable points is modified via one of its 'destructive'
methods. In general, unless an object provides a mutation method, you
can't change it this way.

Note that the *variable* is local to the method body, and doesn't change
which object the outside variable refers to.

martin

···

Tookelso <tookskie-googlegroups@yahoo.com> wrote:

"Tookelso" <tookskie-googlegroups@yahoo.com> writes:

Hello,

Sorry for asking this basic question, but I couldn't find it in the
PickAxe book.

When you pass an argument to a method, is it passed by reference, or by
value?

By value. But remember that the value of a variable is always a
reference! (That's not *technically* true for Fixnums, but since
Fixnums are entirely immutable, the abstraction holds up.)

Or, does it depend on the object's type?

For example, in the simple script below, the program outputs "0".
#-----------------------------------
def test(c_count)
c_count = c_count + 1
end
# begin program
c_count = 0
test(c_count)
puts c_count
#-----------------------------------
I realize that for simple things, it's better to return a value from
the method, but I would like to know *why* it's not modifying my
"c_count" value.

(In this explanation, I'm ignoring the fact that Fixnums are special --
that fact is a pure implementation detail. Just an optimization.)

When you initialize c_count, the 0 is evaluated first. Ruby will
allocate space for a Fixnum. Let's say it allocates some bytes over at
the memory location 0xCAFEBABE. It copies 0x0000, or whatever the
machine representation of 0 is, to 0xCAFEBABE.

Then it creates a new instance variable. Sets its value to 0xCAFEBABE.

When you call ``test'', c_count is passed by value, as always. The
value passed is 0xCAFEBABE.

Now, what happens when you receive an argument is this: a new instance
variable is created. This c_count has nothing in common with the other
c_count -- except that they happen to share values. Of course, you
could name the test parameter ``snuggly_taco'', and the program would
work the same.

When you assign to snuggly_taco, the only thing that happens is that the
value of snuggly_taco is changed, to the address of that new number
you're making. This affects neither 0xCAFEBABE nor c_count.

If you really want to change the value of an instance variable from
another function -- bad luck. You can't.

Actually, I lied. You can do it. If you use Florian Groß's
binding_of_caller and eval.

But that's an extremely obscure hack. Avoid it! The clean solution is,
as always, adding another layer of indirection. Make it so that by
setting something you actually can change -- like an instance variable
-- you change the effective value.

class Box
  attr_accessor :value

  def initialize value=nil
    @value = value
  end
end

def test c_count
  c_count.value = c_count.value + 1
end

c_count = Box.new 0
test c_count
puts c_count

This box is perfectly analogous with a C pointer (except, of course,
that you can't do pointer arithmetic). Instead of ``*foo'', we write
``foo.value''.

(By the way, is there a class like this in the stdlib?)

mikael

Tookelso wrote:

Hello,

Moin!

When you pass an argument to a method, is it passed by reference, or by
value?
Or, does it depend on the object's type?

For example, in the simple script below, the program outputs "0".
#-----------------------------------
def test(c_count)
c_count = c_count + 1
end
# begin program
c_count = 0
test(c_count)
puts c_count
#-----------------------------------
I realize that for simple things, it's better to return a value from
the method, but I would like to know *why* it's not modifying my
"c_count" value.

Arguments in Ruby are just objects that are passed via ref because it would not make much sense to get copies of your Objects all over the place. It's simpler to just request a copy of an Object if you need one. (Via Object#clone or #dup)

But note that variables are no Objects, they are just names for Objects!

So how do you accomplish the above? If you just want to return multiple values use something like this:

def wc(text)
   lines = text.count "\n"
   words = text.scan(/\S+/).size
   bytes = text.size
   return [lines, words, bytes]
end

lines, words, bytes = wc("hello world")

If you really want to do an assignment to a variable after the fact use something like this:

def inc_score(amount = 1, &block)
   block.call(amount)
end

score = 0
score_assign = lambda { |new_score| score = new_score }
inc_score(5, &score_assign)

This uses blocks as a callback for doing the assignment.

If you really want boxed Variables (that can be handled as Objects) you can use the library I attached to do something like this:

def inc(var)
   var.value += 1
end

x = 1
5.times { inc Variable[:x] }
Variable[:x].value # => 6
x # => 6

variable.rb (1000 Bytes)

Mikael Brockman wrote:

"Tookelso" <tookskie-googlegroups@yahoo.com> writes:

When you pass an argument to a method, is it passed by reference, or by
value?

By value. But remember that the value of a variable is always a
reference!

Of all the explanations so far, I liked this one best. Thanks, Mikael!

If Ruby really passed arguments by reference (like e.g. C++), then Tookelso would not be confused. In reality, Ruby passes a copy of the caller's reference. This copy must be handled rather gently, otherwise the object referenced from caller's scope quickly becomes inaccessible.

It's a subtle point, and it's easy to see why people familiar with traditional call-by-reference semantics would be misled. I know I didn't fully appreciate it until now.

···

--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoil.com/&gt;

Hmmm. Is it safe to pass a Hash and modify it in a function then use it?

for example:

a = Hash.new
modify(a)
do_something_useful(a)

after reading the response below, it's making me think how hashs behave and whether it's safe to do the above.

Thanks

Mikael Brockman wrote:

···

"Tookelso" <tookskie-googlegroups@yahoo.com> writes:

Hello,

Sorry for asking this basic question, but I couldn't find it in the
PickAxe book.

When you pass an argument to a method, is it passed by reference, or by
value?

By value. But remember that the value of a variable is always a
reference! (That's not *technically* true for Fixnums, but since
Fixnums are entirely immutable, the abstraction holds up.)

Or, does it depend on the object's type?

For example, in the simple script below, the program outputs "0".
#-----------------------------------
def test(c_count)
c_count = c_count + 1
end
# begin program
c_count = 0
test(c_count)
puts c_count
#-----------------------------------
I realize that for simple things, it's better to return a value from
the method, but I would like to know *why* it's not modifying my
"c_count" value.

(In this explanation, I'm ignoring the fact that Fixnums are special --
that fact is a pure implementation detail. Just an optimization.)

When you initialize c_count, the 0 is evaluated first. Ruby will
allocate space for a Fixnum. Let's say it allocates some bytes over at
the memory location 0xCAFEBABE. It copies 0x0000, or whatever the
machine representation of 0 is, to 0xCAFEBABE.

Then it creates a new instance variable. Sets its value to 0xCAFEBABE.

When you call ``test'', c_count is passed by value, as always. The
value passed is 0xCAFEBABE.

Now, what happens when you receive an argument is this: a new instance
variable is created. This c_count has nothing in common with the other
c_count -- except that they happen to share values. Of course, you
could name the test parameter ``snuggly_taco'', and the program would
work the same.

When you assign to snuggly_taco, the only thing that happens is that the
value of snuggly_taco is changed, to the address of that new number
you're making. This affects neither 0xCAFEBABE nor c_count.

If you really want to change the value of an instance variable from
another function -- bad luck. You can't.

Actually, I lied. You can do it. If you use Florian Groß's
binding_of_caller and eval.

But that's an extremely obscure hack. Avoid it! The clean solution is,
as always, adding another layer of indirection. Make it so that by
setting something you actually can change -- like an instance variable
-- you change the effective value.

> class Box
> attr_accessor :value
> > def initialize value=nil
> @value = value
> end
> end
> > def test c_count
> c_count.value = c_count.value + 1
> end
> > c_count = Box.new 0
> test c_count
> puts c_count

This box is perfectly analogous with a C pointer (except, of course,
that you can't do pointer arithmetic). Instead of ``*foo'', we write
``foo.value''.

(By the way, is there a class like this in the stdlib?)

mikael

"Glenn Parker" <glenn.parker@comcast.net> schrieb im Newsbeitrag news:41DA98F9.1040001@comcast.net...

Mikael Brockman wrote:

"Tookelso" <tookskie-googlegroups@yahoo.com> writes:

When you pass an argument to a method, is it passed by reference, or by
value?

By value. But remember that the value of a variable is always a
reference!

Of all the explanations so far, I liked this one best. Thanks, Mikael!

+1

Regards

    robert

Thanks to all for your explanations. This helped clear up my
confusion.

I will use the return values of the methods to modify values, like in
Perl.

--Nate

Nash Kabbara wrote:

Hmmm. Is it safe to pass a Hash and modify it in a function then use it?

for example:

a = Hash.new
modify(a)
do_something_useful(a)

after reading the response below, it's making me think how hashs behave and whether it's safe to do the above.

Sure, it's safe, and it (probably) does exactly what you would expect.

Ignore the following if you don't want to entertain my rambling mind.

What if Hash was immutable (like Fixnum)?

class Hash
   private :=

   def assign(key, val)
     copy = self.clone
     copy[key] = val
     return copy
   end
end

x = { "a" => 1 }
y = x
x = x.assign("a", 3)
# y["a"] is still 1

Not terribly useful or efficient. :slight_smile:

Note: I think it would be more useful if Ruby's built-in assignment operator evaluated to the LHS instead of the RHS.

What if we want to go the other way and make Fixnum mutable (like Hash)? Ruby won't override basic assignment (=), so use =~ instead.

class MutableFixnum
   def initialize(v)
     @value = v.to_i
   end
   def =~(v)
     @value = v.to_i
   end
   def +(a)
     @value + a.to_i
   end
   def inspect
     @value.to_s
   end
end

class Fixnum
   def to_mutable
     return MutableFixnum.new(self)
   end
end

x = 1.to_mutable
y = x
x =~ x + 1
p y => 2

What fun! But maybe not what you would have expected.

Apparently, we prefer it when variables refering to the same Hash all change when one of those variables is operated on. Yet, it feels strange when variables refering to the same "number" all change when one of those variables is operated on.

···

--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoil.com/&gt;

Also I suppose you understood one must dup objects explicitly if a method
will trash them. That's the other important half, of course.