What is a good way to access instances? - ruby

How do I write my class to access array for different objects?
class ListArray
attr_accessor :arr
def initialize arr
#arr = arr
end
end
a = ListArray.new([0, 1, 2, 3])
b = ListArray.new(a.arr)
a.arr[2] = 999
b.arr[2] = 4
a.arr[2] #=> 4 ?
b.arr[2] #=> 4
When I changed value of b.arr[2] to 4, a.arr[2] value (which should be 999) takes the value 4.
I don't know what I did wrong.
[edit]
My full code looks more like this:
class OtherClass
def list
end
class ListArray
attr_accessor :arr
def initialize arr
#arr = arr
end
def putItem ...
def getItem ...
def cutList &bloc ...
end
a = ListArray.new obj1_other_class.list
# obj2_other_class.list => [[1, 2], [3, 4], ... ]
# [3, 4] is an item
b = ListArray.new obj2_other_class.list
a.putItem [5, 6]
c = ListArray.new a.arr
c.arr += b.arr
c.arr[1][0] = 7
...
How can I avoid object's same id problems?

class ListArray
attr_accessor :arr
def initialize(arr)
#arr = arr
end
def arr_object_id
#arr.object_id
end
end
Create an instance of ListArray, creating an instance variable #arr equal to [0, 1, 2, 3]:
a = ListArray.new [0, 1, 2, 3]
#=> #<ListArray:0x0000574d960e19e8 #arr=[0, 1, 2, 3]>
Let's check the value of #arr and retrieve its object id:
a.arr
#=> [0, 1, 2, 3]
a.arr_object_id
#=> 47995370802440
Now create another instance of ListArray, creating its instance variable #arr and setting it equal to the value of a.arr:
b = ListArray.new(a.arr)
#=> #<ListArray:0x0000574d9611bdf0 #arr=[0, 1, 2, 3]>
b.arr
#=> [0, 1, 2, 3]
b.arr_object_id
#=> 47995370802440
The interesting thing here is that a.arr_object_id == b.arr_object_id. That's not surprising, however, because we initialized b's instance variable to a's instance variable, so they are the same object!
Next, change the value of a's instance variable to [0, 1, 999, 3]:
a.arr[2] = 999
a.arr
#=> [0, 1, 999, 3]
a.arr_object_id
#=> 47995370802420
Check if the value of b's instance variable has changed:
b.arr
#=> [0, 1, 999, 3]
b.arr_object_id
#=> 47995370802440
It has, because a's and b's instance variables #arr hold the same object.
To make b's instance variable hold an array whose instance variables are the same as a's, but make the two arrays different objects, create b with its instance variable #arr equal to a copy of the value of a's instance variable:
a = ListArray.new [0, 1, 2, 3]
#=> #<ListArray:0x0000574d9610d818 #arr=[0, 1, 2, 3]>
a.arr_object_id
#=> ...320
b = ListArray.new(a.arr.dup)
#=> #<ListArray:0x0000574d961143c0 #arr=[0, 1, 2, 3]>
b.arr
#=> [0, 1, 2, 3]
b.arr_object_id
#=> ...100 (different than a.arr_object_id)
a.arr[2] = 19
a.arr
#=> [0, 1, 19, 3]
b.arr
#=> [0, 1, 2, 3]
But, wait, we're not finished. Here's a second example that illustrates why you cannot always merely apply dup.
a = ListArray.new [0, [1, 2], 3]
#=> #<ListArray:0x0000574d9614b370 #arr=[0, [1, 2], 3]>
a.arr_object_id
#=> ...700
a.arr[1].object_id
#=> ...720
a.arr[1][1].object_id
#=> 5
2.object_id
#=> 5
b = ListArray.new(a.arr.dup)
#=> #<ListArray:0x0000574d96119258 #arr=[0, [1, 2], 3]>
b.arr
#=> [0, [1, 2], 3]
b.arr_object_id
#=> ...160 (different than a.arr_object_id)
b.arr[1].object_id
#=> ...720 (same as a.arr[1].object_id)
b.arr[1][1].object_id
#=> 5
Now change the value of a.arr[1][1]:
a.arr[1][1] = 9
a.arr
#=> [0, [1, 9], 3] (as expected)
a.arr[1].object_id
#=> ...720 (no change)
b.arr
#=> [0, [1, 9], 3]
b.arr[1].object_id
#=> ...720 (no change)
You see this changes b[1][1] as well. That's because the contents of the object that is the value of both a.arr[1] and b.arr[1] has been altered. Now try this.
a.arr[1] = [8, 0]
a.arr
#=> [0, [8, 0], 3] (as expected)
a.arr[1].object_id
#=> ...880 (a new object!)
b.arr
#=> [0, [1, 9], 3] (unchanged!)
b.arr[1].object_id
#=> ...720 (unchanged)
For this example we would need to write:
a = ListArray.new [0, [1, 2], 3]
b = ListArray.new(a.arr.dup.map { |e| e.dup })
a.arr[1][1] = 9
a.arr
#=> [0, [1, 9], 3]
b.arr
#=> [0, [1, 2], 3] (no change!)
a.arr.dup.map { |e| e.dup } is referred to as a deeper copy of a.arr than is a.arr.dup. If there were yet deeper nested arrays ([1, [2, [3, 4]], 5]) we would have to dup to lower levels of a.arr. For a Ruby newbie it's not important to fully understand how deep copies are constructed, merely that they are needed to achieve independence for duped copies of objects.

By
b = ListArray.new a.arr
You actually transfer the reference of a.arr to b, not it's value.
You can do it like this:
b = ListArray.new a.arr.dup
Check this question, which is about the argument passing of reference or value.

Related

Why a new call of a method with exclamation mark affects all previous calls of that method?

I'm sorry if this is a duplicate - I couldn't find anything similar in the existing posts.
I understand the difference between methods like shuffle and shuffle!. However, I am confused why calling the method more than once would result in changing the variables of all objects that previously referred to it? I'd expect once we apply a method, that the variable gets a value and we're done with it. Not that it continues to refer to the method call and the argument passed and that it would get re-evaluated later on.
I thought it's best to demonstrate with an example:
irb(main):001:1* def shuffle(arr)
irb(main):002:1* arr.shuffle!
irb(main):003:0> end
=> :shuffle
irb(main):004:0> arr = [1,2,3,4]
=> [1, 2, 3, 4]
irb(main):005:0> one = shuffle(arr)
=> [4, 2, 3, 1]
irb(main):006:0> two = shuffle(arr)
=> [1, 2, 4, 3]
irb(main):007:0> one
=> [1, 2, 4, 3]
So, here I'd expect one to stay [4, 2, 3, 1]. However, with each new call, all previous ones would get equated to the latest result of the method call. I realise it should have something to do with calling it with the same argument arr, but still doesn't quite make sense.
Array#shuffle! shuffles the array in-place and returns its receiver:
ary = [1, 2, 3, 4]
ary.equal?(ary.shuffle!) #=> true
Assigning the result from shuffle! to another variable doesn't change this. It merely results in two variables referring to the same array:
a = [1, 2, 3, 4]
b = a.shuffle!
a #=> [2, 4, 1, 3]
b #=> [2, 4, 1, 3]
a.equal?(b) #=> true
You probably want a new array. That's what Array#shuffle (without !) is for:
a = [1, 2, 3, 4]
b = a.shuffle
a #=> [1, 2, 3, 4]
b #=> [2, 4, 1, 3]
Even if shuffle returns the element in the original order, you'll get another array instance:
a = [1, 2, 3, 4]
b = a.shuffle until b == a
a #=> [1, 2, 3, 4]
b #=> [1, 2, 3, 4]
a.equal?(b) #=> false

How can I get the next n number of elements using a Ruby enumerator?

I am trying to get the next n number of elements using a Ruby enumerator, with this:
a = [1, 2, 3, 4, 5, 6]
enum = a.each
enum.next(2) # expecting [1, 2]
enum.next(2) # expecting [3, 4]
But #next does not support that. Is there another way that I can do that?
Or shall I do?
What is the correct Ruby way to do that?
You can use take method
enum.take(2)
If you need slices of two elements, you could do:
e = enum.each_slice(2)
p e.next
#=> [1, 2]
p e.next
#=> [3, 4]
a = [1, 2, 3, 4, 5, 6]
enum = a.dup
enum.shift(2) # => [1, 2]
enum.shift(2) # => [3, 4]

New array declaration in Ruby

In Ruby, what is the difference between:
new_array = old_array
and
new_array = Array.new(old_array)
The first one is assignment, and will not create a new object. The second one is invalid (under default definition of Object), and will raise an argument error.
Consider the following:
old_arr = [1,2,3] #=> [1, 2, 3]
old_arr.object_id #=> 70142672048160
new_arr = old_arr #=> [1, 2, 3]
new_arr.object_id #=> 70142672048160
newer_arr = Array.new(old_arr) #=> [1, 2, 3]
newer_arr.object_id #=> 70142675883800
So you see the local variables old_arr and new_arr hold the same array object. By contrast, the variable newer_arr holds a different array object.
Now try this:
old_arr[0] = 'X' #=> "X"
old_arr #=> ["X", 2, 3]
then
new_arr #=> ["X", 2, 3]
newer_arr #=> [1, 2, 3]
newer_arr points to what is called a "shallow copy" of old_arr. To see what this means, consider another example:
old_arr = [1,2,[3,4]] #=> [1, 2, [3, 4]]
new_arr = old_arr #=> [1, 2, [3, 4]]
newer_arr = Array.new(old_arr) #=> [1, 2, [3, 4]]
old_arr[0] = 'X' #=> "X"
old_arr[2][0] = 'Y' #=> "Y"
old_arr #=> ["X", 2, ["Y", 4]]
new_arr #=> ["X", 2, ["Y", 4]]
newer_arr #=> [1, 2, ["Y", 4]]
As before, newer_arr[0] has not been modified, and newer_arr[2] contains the same object as it did previously, but that object has been modified.
"what happens in case we use an Array or String instead of Object?"
That depends on the class definition itself, so you should look at the documentation.
Basically, #new method creates a new object and then calls "initialize" method with parameters passed to it.
To answer your question:
Array.new method copies the array passed to it (actually, as i said before: it passes that array to initialize method, which copies it).
Same happens with String.
The easiest way to know such things is actually repl.
In irb execute following lines, one by one:
a = ":)"
b = a
a[1] = '('
puts a
puts b
a = ":)"
b = String.new(a)
a[1] = '('
puts a
puts b
a = [1,2,3]
b = a
a[1] = 10
puts a
puts b
a = [1,2,3]
b = Array.new(a)
a[1] = 10
puts a
puts b

Change value of a cloned object

I have an object that has an instance variable array called my_array. I declare it via attr_accessor.
my_object.my_array = [1,2,3] # <= I don't know the max size of my_array(it can be dynamic)
I want to create the same object with my_object and fill its my_array with only one element. The value inside that element is each value from my_array's element (from my_object). Because the size of my_array is dynamic I suppose I need to iterate it via each.
Here's my attempt:
my_object.my_array.each do |element| # <= my_object is a special object
new_object = nil unless new_object.nil?
new_object = my_object.clone # <= I create new object with same class with my_object
new_object.my_array.clear # <= clear all element inside it.
new_object.my_array.push(element) # assign element value to the first element.
# rest of code #
new_object = nil
end
The iteration is not iterating properly. The size of my_object.my_array is 3, then it should iterating three times, but it's not, it's only iterating one time. I believe it's because of new_object.my_array.clear, but I cloned it from my_object, so why this happened?
When you assign one array it just copies the reference and both of them point to the same reference,
so a change in one is reflected when you print either of them:
orig_array = [1,2,3,4]<br>
another_array = orig_array
puts orig_array.unshift(0).inspect
puts another_array.inspect
Which outputs:
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]
To avoid this you can use Marshal to copy from the original array without impacting the object to which it is copied.
Any changes in the original array will not change the object to which it is copied.
orig_array = [1,2,3,4]
another_array = Marshal.load(Marshal.dump(orig_array))
puts orig_array.unshift(0).inspect
puts another_array.inspect
Which outputs:
[0, 1, 2, 3, 4]
[1, 2, 3, 4]
The problem is, that clone will make a shallow clone, not a deep clone. In other words, my_array is a reference and the cloned instance references the same array in memory. Consider:
class MyClass
attr_accessor :my_array
end
a = MyClass.new
a.my_array = [1, 2, 3]
a.my_array
#=> [1, 2, 3]
b = a.clone
b.my_array.push(4)
b.my_array
#=> [1, 2, 3, 4]
a.my_array # also changed!
#=> [1, 2, 3, 4]
In order to fix this, you need to extend the initialize_copy method to also clone the array:
class MyClass
attr_accessor :my_array
def initialize_copy(orig)
super
self.my_array = orig.my_array.clone
end
end
a = MyClass.new
a.my_array = [1, 2, 3]
a.my_array
#=> [1, 2, 3]
b = a.clone
b.my_array.push(4)
b.my_array
#=> [1, 2, 3, 4]
a.my_array # did not change, works as expected
#=> [1, 2, 3]

Ruby enumerator chaining

In this example,
[1, 2, 3].each_with_index.map{|i, j| i * j}
# => [0, 2, 6]
my understanding is that, since each_with_index enumerator is chained to map, map behaves like each_with_index by passing an index inside the block, and returns a new array.
For this,
[1, 2, 3].map.each_with_index{|i, j| i * j}
# => [0, 2, 6]
I'm not sure how to I interpret it.
In this example,
[1, 2, 3, 4].map.find {|i| i == 2}
# => 2
I was expecting the the output to be [2], assuming that map is chained to find, and map would return a new array.
Also, I see this:
[1, 2, 3, 4].find.each_with_object([]){|i, j| j.push(i)}
# => [1]
[1, 2, 3, 4].each_with_object([]).find{|i, j| i == 3}
# => [3, []]
Can you let me know how to interpret and understand enumerator chains in Ruby?
You might find it useful to break these expressions down and use IRB or PRY to see what Ruby is doing. Let's start with:
[1,2,3].each_with_index.map { |i,j| i*j }
Let
enum1 = [1,2,3].each_with_index
#=> #<Enumerator: [1, 2, 3]:each_with_index>
We can use Enumerable#to_a (or Enumerable#entries) to convert enum1 to an array to see what it will be passing to the next enumerator (or to a block if it had one):
enum1.to_a
#=> [[1, 0], [2, 1], [3, 2]]
No surprise there. But enum1 does not have a block. Instead we are sending it the method Enumerable#map:
enum2 = enum1.map
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:each_with_index>:map>
You might think of this as a sort of "compound" enumerator. This enumerator does have a block, so converting it to an array will confirm that it will pass the same elements into the block as enum1 would have:
enum2.to_a
#=> [[1, 0], [2, 1], [3, 2]]
We see that the array [1,0] is the first element enum2 passes into the block. "Disambiguation" is applied to this array to assign the block variables the values:
i => 1
j => 0
That is, Ruby is setting:
i,j = [1,0]
We now can invoke enum2 by sending it the method each with the block:
enum2.each { |i,j| i*j }
#=> [0, 2, 6]
Next consider:
[1,2,3].map.each_with_index { |i,j| i*j }
We have:
enum3 = [1,2,3].map
#=> #<Enumerator: [1, 2, 3]:map>
enum3.to_a
#=> [1, 2, 3]
enum4 = enum3.each_with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:map>:each_with_index>
enum4.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum4.each { |i,j| i*j }
#=> [0, 2, 6]
Since enum2 and enum4 pass the same elements into the block, we see this is just two ways of doing the same thing.
Here's a third equivalent chain:
[1,2,3].map.with_index { |i,j| i*j }
We have:
enum3 = [1,2,3].map
#=> #<Enumerator: [1, 2, 3]:map>
enum3.to_a
#=> [1, 2, 3]
enum5 = enum3.with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:map>:with_index>
enum5.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum5.each { |i,j| i*j }
#=> [0, 2, 6]
To take this one step further, suppose we had:
[1,2,3].select.with_index.with_object({}) { |(i,j),h| ... }
We have:
enum6 = [1,2,3].select
#=> #<Enumerator: [1, 2, 3]:select>
enum6.to_a
#=> [1, 2, 3]
enum7 = enum6.with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:select>:with_index>
enum7.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum8 = enum7.with_object({})
#=> #<Enumerator: #<Enumerator: #<Enumerator: [1, 2, 3]:
# select>:with_index>:with_object({})>
enum8.to_a
#=> [[[1, 0], {}], [[2, 1], {}], [[3, 2], {}]]
The first element enum8 passes into the block is the array:
(i,j),h = [[1, 0], {}]
Disambiguation is then applied to assign values to the block variables:
i => 1
j => 0
h => {}
Note that enum8 shows an empty hash being passed in each of the three elements of enum8.to_a, but of course that's only because Ruby doesn't know what the hash will look like after the first element is passed in.
Methods you are mentioning are defined on Enumerable objects. These methods behave differently depending on whether you pass a block or not.
When you do not pass a block, they typically return an Enumerator object, to which you can chain further methods like each_with_index, with_index, map, etc.
When you pass a block to these methods, they return different kinds of object depending on what will make sense for that particular method.
For methods like find, its purpose is to find the first object that satisfies a condition, and it does not make particular sense to wrap that in an array, so it returns that object bare.
For methods like select or reject, their purpose is to return all relevant objects, so they cannot return a single object, and they have to be wrapped in an array (even when the relevant object happens to be a single object).

Resources