Calling by Reference - Two Questions

I know I'm not the first person to get stumped by how to get Ruby to
change an object in a method.

I want to do:

method a1 or a1.method

I can do

a1 = method a1

but amongst other things that creates a new object.

a1 = [1,2]
puts " #{a1} # 12

def myswap1(a2)
  a3 = [0,0]
  a3[0] = a2[1]
  a3[1] = a2[0]
  a2 = a3
  end

myswap1 a1
puts " #{a1} " # 12

Ruby passes in a1, so at the start of the method a2 is the same
reference as a1. However when I assign a1 = a3, a1 becomes a different
reference and disappears when the method terminates.

So my first question is why won't Ruby let me do this:

a1 = [1,2]

public
def myswap5
  a3 = [0,0]
  a3[0] = self[1]
  a3[1] = self[0]
  self = a3
  end

a1.myswap5 # gives error message: Can't change the value of self

My second question is from this:

a1 = [1,2]

def myswap6(a2)
  a2.reverse!
  end

  myswap6 a1
  puts " #{a1}" #21

What magic is reverse! using, and how can I avail myself of it?

···

--
Posted via http://www.ruby-forum.com/.

I think I didn't understand your question correctly, sorry (My english
skills are not so good).
But, see bellow and tell me if this is what you want.

def myswap(arr)
  [arr[1], arr[0]]
end

def myswap!(arr)
  arr[0], arr[1] = arr[1], arr[0]
end

def myswap2!(arr)
  arr[0], arr[1] = arr[1], arr[0]
  arr
end

a = [2, 5]

myswap a # => [5,2] (new object)
myswap! a # => [5,2] (a now is changed; note that the returned array is not a)
myswap2! a # => [5,2] (a now is changed; the returned array is "a"
(same object_id) )

Abinoam Jr.

···

On Mon, Jan 17, 2011 at 9:07 PM, Mike Stephens <rubfor@recitel.net> wrote:

I know I'm not the first person to get stumped by how to get Ruby to
change an object in a method.

I want to do:

method a1 or a1.method

I can do

a1 = method a1

but amongst other things that creates a new object.

a1 = [1,2]
puts " #{a1} # 12

def myswap1(a2)
a3 = [0,0]
a3[0] = a2[1]
a3[1] = a2[0]
a2 = a3
end

myswap1 a1
puts " #{a1} " # 12

Ruby passes in a1, so at the start of the method a2 is the same
reference as a1. However when I assign a1 = a3, a1 becomes a different
reference and disappears when the method terminates.

So my first question is why won't Ruby let me do this:

a1 = [1,2]

public
def myswap5
a3 = [0,0]
a3[0] = self[1]
a3[1] = self[0]
self = a3
end

a1.myswap5 # gives error message: Can't change the value of self

My second question is from this:

a1 = [1,2]

def myswap6(a2)
a2.reverse!
end

myswap6 a1
puts " #{a1}" #21

What magic is reverse! using, and how can I avail myself of it?

--
Posted via http://www.ruby-forum.com/\.

Well, that is bizarre.

In 1.8.6 I get:

def myswap(arr)
  [arr[1], arr[0]]
end

def myswap!(arr)
  arr[0], arr[1] = arr[1], arr[0]
end

def myswap2!(arr)
  arr[0], arr[1] = arr[1], arr[0]
  arr
end

a = [2, 5]
puts " start #{a} #{a.object_id} " #start 25 27431650

myswap a # => [5,2] (new object)
puts " myswap #{a} #{a.object_id} " myswap 25 27431650

myswap! a # => [5,2] (a now is changed; note that the returned array
isnot a)
puts " myswap! #{a} #{a.object_id} " myswap! 52 27431650

myswap2! a
puts " myswap2! #{a} #{a.object_id} " myswap2! 25 27431650

I have no idea why myswap! works and myswap2! doesn't.

···

--
Posted via http://www.ruby-forum.com/.

Interesting.

I'm trying to see the pattern here. Ruby doesn't change arr's object if
it is refering to elements already present. However (I can only test
this in tryruby at this moment) it does change the object and therefore
lose the change if I do:

def myswapx(arr)
arr[2] = 3
end

So what's the rule?

···

--
Posted via http://www.ruby-forum.com/.

So my first question is why won't Ruby let me do this:

a1 = [1,2]

public
def myswap5
a3 = [0,0]
a3[0] = self[1]
a3[1] = self[0]
self = a3
end

a1.myswap5 # gives error message: Can't change the value of self

You actually can (though I'm not sure what you think is happening when you
say self=a3, so maybe it is not exactly the same):

ary = [1,2]

def ary.swap
  new_contents = [ self[1] , self[0] ]
  replace new_contents
end

ary # => [1, 2]
ary.swap
ary # => [2, 1]

ary replaced its contents with new_contents

My second question is from this:

a1 = [1,2]

def myswap6(a2)
a2.reverse!
end

myswap6 a1
puts " #{a1}" #21

What magic is reverse! using, and how can I avail myself of it?

It is written in C, the core being (at least for 1.8):

    p1 = RARRAY(ary)->ptr;
    p2 = p1 + RARRAY(ary)->len - 1; /* points last item */
    while (p1 < p2) {
        tmp = *p1;
        *p1++ = *p2;
        *p2-- = tmp;
    }

You could replicate it in Ruby like this:

ary = [1,2,3,4]

def ary.my_reverse
  lower , higher = 0 , size-1
  while lower < higher
    self[lower] , self[higher] = self[higher] , self[lower]
    lower += 1
    higher -= 1
  end
  self
end

ary.my_reverse # => [4, 3, 2, 1]
ary.my_reverse # => [1, 2, 3, 4]
ary << 5
ary.my_reverse # => [5, 4, 3, 2, 1]
ary.my_reverse # => [1, 2, 3, 4, 5]

···

On Mon, Jan 17, 2011 at 6:07 PM, Mike Stephens <rubfor@recitel.net> wrote:

They both work. Remember that myswap!(a) changed the array to [5,2] and so myswap2! changes it back to [2,5].

Gary Wright

···

On Jan 17, 2011, at 10:05 PM, Mike Stephens wrote:

Well, that is bizarre.

In 1.8.6 I get:

def myswap(arr)
[arr[1], arr[0]]
end

def myswap!(arr)
arr[0], arr[1] = arr[1], arr[0]
end

def myswap2!(arr)
arr[0], arr[1] = arr[1], arr[0]
arr
end

a = [2, 5]
puts " start #{a} #{a.object_id} " #start 25 27431650

myswap a # => [5,2] (new object)
puts " myswap #{a} #{a.object_id} " myswap 25 27431650

myswap! a # => [5,2] (a now is changed; note that the returned array
isnot a)
puts " myswap! #{a} #{a.object_id} " myswap! 52 27431650

myswap2! a
puts " myswap2! #{a} #{a.object_id} " myswap2! 25 27431650

I have no idea why myswap! works and myswap2! doesn't.

The rule is how Ruby handles variables and objects. A local variable
holds a reference to an object. When you call a method passing that
variable, the reference gets copied into a new local variable, that is
only accessible in the method:

def m variable_local_to_method_m

end

a = [1,2,3]
m(a)

Here, variable_local_to_method_m and a reference the same object, the
array [1,2,3], but within the method you cannot make a reference a
different object, because what gets passed to the method is a copy of
the reference.

If the object has mutable method, calling those methods through the
variable_local_to_method_m will result in the object changing its
state, which is obviously visible outside the method, when you access
the object through variable a. For example:

irb(main):001:0> class Item
irb(main):002:1> def initialize
irb(main):003:2> @count = 0
irb(main):004:2> end
irb(main):005:1> def increment
irb(main):006:2> @count += 1
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0> def m item
irb(main):010:1> item.increment
irb(main):011:1> end
=> nil
irb(main):012:0> a = Item.new
=> #<Item:0xb73f5ab8 @count=0>
irb(main):013:0> m a
=> 1
irb(main):014:0> a
=> #<Item:0xb73f5ab8 @count=1>

As you can see, when we call the increment method using the item
variable inside the method m, the object changes its @count instance
variable. Both item and a reference the same object, so after the
method, a still references the same object which has a different state
now.

In your example the array [1,2,3] is the object whose reference is
being passed to methods. An array is a mutable object, and so when you
call a method like = you are changing the object that is referenced
by the outside variable a.

Jesus.

···

On Tue, Jan 18, 2011 at 8:47 AM, Mike Stephens <rubfor@recitel.net> wrote:

Interesting.

I'm trying to see the pattern here. Ruby doesn't change arr's object if
it is refering to elements already present. However (I can only test
this in tryruby at this moment) it does change the object and therefore
lose the change if I do:

def myswapx(arr)
arr[2] = 3
end

So what's the rule?

Josh Cheek wrote

def ary.swap
  new_contents = [ self[1] , self[0] ]
  replace new_contents
end

That kind of just moves the question on to how does replace work. But
thank you for showing me replace, and even more for opening my eyes to
an exciting world of singletons.

def ary.my_reverse
  lower , higher = 0 , size-1
  while lower < higher
    self[lower] , self[higher] = self[higher] , self[lower]
    lower += 1
    higher -= 1
  end
  self
end

That does work (and doesn't need the last self). It seems to agree with
the Jesus rule, that you can assign to self but only
component-by-component.

I guess your answers give me everything I would ever need!

···

--
Posted via http://www.ruby-forum.com/\.

They both work. Remember that myswap!(a) changed the array to [5,2] and so myswap2! changes it back to [2,5].

Gary Wright

Exactly.

One more thing, in the first try with myswap the returned value is
being ignored.

> myswap a # => [5,2] (new object)
> puts " myswap #{a} #{a.object_id} " myswap 25 27431650

Assign the returned value to a variable.

b = myswap a

Now b is the "swap" of a and they have different object_ids.

Abinoam Jr.

···

On Tue, Jan 18, 2011 at 12:34 AM, Gary Wright <gwtmp01@mac.com> wrote:

Josh Cheek wrote

def ary.swap
new_contents = [ self[1] , self[0] ]
replace new_contents
end

That kind of just moves the question on to how does replace work. But
thank you for showing me replace, and even more for opening my eyes to
an exciting world of singletons.

def ary.my_reverse
lower , higher = 0 , size-1
while lower < higher
self[lower] , self[higher] = self[higher] , self[lower]
lower += 1
higher -= 1
end
self
end

That does work (and doesn't need the last self).

The last "self" is the method's return value. It makes perfect sense
to return self here to have a defined return value and not leak some
implementation state. Btw, one could do

class Array
  def my_reverse
    (size / 2).times do |i|
      j = size - 1 - i
      self[i], self[j] = self[j], self[i]
    end

    self
  end
end

It seems to agree with
the Jesus rule, that you can assign to self but only
component-by-component.

You cannot assign to self!

14:15:00 ~$ ruby19 -e 'self = 1'
-e:1: Can't change the value of self
self = 1
      ^
14:15:03 ~$

Calling self#= is something different - this is actually a method
that must be implemented for the object that self points to.

Kind regards

robert

···

On Tue, Jan 18, 2011 at 2:06 PM, Mike Stephens <rubfor@recitel.net> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Oh yeah, I should have made that more clear. Inside the method, self is the
array itself. So self[index] inside the method is the same as ary[self]
outside the method.

···

On Tue, Jan 18, 2011 at 7:16 AM, Robert Klemme <shortcutter@googlemail.com>wrote:

Calling self#= is something different - this is actually a method
that must be implemented for the object that self points to.

Robert Klemme wrote
You cannot assign to self!
Calling self#= is something different - this is actually a method
that must be implemented for the object that self points to.

Robert, can you expand on that?

My example has an object of type Array. Array has a method =
implemented, so in what way is = different to = ? One works on self;
the other raises an error.

···

--
Posted via http://www.ruby-forum.com/\.

*ary[self] should be ary[index]

···

On Tue, Jan 18, 2011 at 7:31 AM, Josh Cheek <josh.cheek@gmail.com> wrote:

On Tue, Jan 18, 2011 at 7:16 AM, Robert Klemme <shortcutter@googlemail.com > > wrote:

Calling self#= is something different - this is actually a method
that must be implemented for the object that self points to.

Oh yeah, I should have made that more clear. Inside the method, self is the
array itself. So self[index] inside the method is the same as ary[self]
outside the method.

= is not a method of object, is the assignment operator. Modifies
which object is referenced be the variable in the left hand side.

Jesus.

···

On Tue, Jan 18, 2011 at 7:01 PM, Mike Stephens <rubfor@recitel.net> wrote:

Robert Klemme wrote
You cannot assign to self!
Calling self#= is something different - this is actually a method
that must be implemented for the object that self points to.

Robert, can you expand on that?

My example has an object of type Array. Array has a method =
implemented, so in what way is = different to = ? One works on self;
the other raises an error.

Mike Stephens wrote in post #975795:

Robert Klemme wrote
You cannot assign to self!
Calling self#= is something different - this is actually a method
that must be implemented for the object that self points to.

Robert, can you expand on that?

My example has an object of type Array. Array has a method =
implemented, so in what way is = different to = ? One works on self;
the other raises an error.

Array doesn't have an '=' method. When you write

    foo[bar] = baz

this is just syntactic sugar for the following method call:

    foo.send(:=, bar, baz)

i.e. the name of the method being called is '=' and we are passing it
two arguments, bar and baz.

So it's not an assignment. It's a method call; the implementation of
this method may mutate the object (or may not).

Try this test program:

class Foo
  def =(*args)
    puts "You sent #{args.inspect}"
  end
end
f = Foo.new
f["hello"] = "world"

Almost all things which look like operators in Ruby are actually method
calls, and hence can be redefined. The exceptions are assignment (to
local variables) and short-circuit boolean operators. Unary "!" can't be
redefined in ruby 1.8, but I think it can in 1.9.

···

--
Posted via http://www.ruby-forum.com/\.