1

I have the following code as an example.

a = [2]
b = a
puts a == b
a.each do |num|
  a[0] = num-1
end
puts a == b 

I want b to refer to a's value, and the value of b not to change when a is changed.(The second puts should return false). Thank you in advance.

Edited-

The answer posted by user2864740 seems to work for the example I gave. However, I'm working on a sudoku solving program, and it doesn't seem to work there.

@gridbylines = [[1,0,0,9,2,0,0,0,0],
           [5,2,4,0,1,0,0,0,0],
           [0,0,0,0,0,0,0,7,0],
           [0,5,0,0,0,8,1,0,2],
           [0,0,0,0,0,0,0,0,0],
           [4,0,2,7,0,0,0,9,0],
           [0,6,0,0,0,0,0,0,0],
           [0,0,0,0,3,0,9,4,5],
           [0,0,0,0,7,1,0,0,6]]

save = Array.new(@gridbylines)     #or @gridbylines.dup
puts save == @gridbylines          #returns true
puts save.equal?(@gridbylines)     #returns false
@gridbylines[0][0] = 'foo'
puts save.equal?(@gridbylines)     #returns false
puts save == @gridbylines          #returns true, but I want "save" not to change when I change "@gridbylines"

Does this have something to do with the fact that I'm using a global variable, or the version of Ruby I'm using, or even because it's a multidimentional array unlike the previous example?

BenMorel
  • 34,448
  • 50
  • 182
  • 322
geckods
  • 35
  • 5
  • it is unclear what you are asking here? have you tried running this code? have you seen an error? if yes, post the error message and clarify your question – NirMH May 21 '14 at 18:44
  • I also didn't understand the question, which is way too unclear. – sidney May 21 '14 at 19:39
  • The title is unclear .. the problem presented (ignoring the title) is fairly clear and quite common. – user2864740 May 22 '14 at 07:59

1 Answers1

3

Variables name or "refer to" objects1. In the code above the same object (which has two names, a and b) is being changed.

A simple solution in this case is to make a (shallow) copy of the original Array object, such as b = a.dup or b = Array.new(a). (With a shallow copy, elements in the array are also shared and will exhibit the similar phoneme as the original question unless they to are [recursively] duplicated, etc.2)

a = [2]
b = Array.new(a)     # create NEW array object, a shallow-copy of `a`
puts a == b          # true   (same content)
puts a.equal?(b)     # false  (different objects)
a.each do |num|
  a[0] = num-1       # now changing the object named by `a` does not
                     #   affect the object named by `b` as they are different
end
puts a == b          # false  (different content)

And an isolated example of this "naming" phenomena (see the different equality forms):

a = []

b = a                # assignment does NOT make a copy of the object
a.equals?(b)         # true    (same object)

c = a.dup            # like Array.new, create a new shallow-copy object
a.equals?(c)         # false   (different object)

1 I find it most uniform to talk about variables being names, as such a concept can be applied across many languages - the key here is that any object can have one or more names, just as a person can have many nicknames. If an object has zero names then it is no longer strongly reachable and, just like a person, is forgotten.

However, another way to view variables (naming objects) is that they hold reference values, where the reference value identifies an object. This leads to phrasing such as

  • "variable a contains a reference [value] to object x" - or,
  • "variable a refers to / references object x" - or, as I prefer,
  • "variable a is a name for object x".

For the case or immutable "primitive" or "immediate" values the underlying mechanics are slightly different but, being immutable the object values cannot be changed and such a lack-of-shared object nature will not manifest itself.


See also:


2 As per the updated question with nested arrays, this is explained by the previous rules - the variables (well, really expressions) still name shared objects. In any case, one way to "clone an array of arrays" (to two levels, although not recursively) is to use:

b = a.map {|r| r.dup}

This is because Array#map returns a new array with the mapped values which are, in this case, duplicates (shallow clones) of the corresponding nested arrays.

See How to create a deep copy of an object in Ruby? for other "deep[er] copy" approaches - especially if the arrays (or affect mutable objects) were nested to N-levels.

Community
  • 1
  • 1
user2864740
  • 60,010
  • 15
  • 145
  • 220
  • 1
    As a more general solution, I suggest [#dup](http://ruby-doc.org/core-2.1.2/Object.html#method-i-dup) to make a shallow copy. – Daniël Knippers May 21 '14 at 19:11
  • @DaniëlKnippers Thanks for the feedback, I've incorporated it into the answer. – user2864740 May 21 '14 at 19:13
  • @user2864740 Please check out my edited question and see if you can help. – geckods May 22 '14 at 06:32
  • @geckods Pat attention to "With a shallow copy, mutable elements are also shared and will exhibit the similar phenome as the original question unless they to are [recursively] duplicated, etc." - In your update the shallow copy does *not* make a copy of the nested objects, and the behavior is explained per the rules above! – user2864740 May 22 '14 at 07:50
  • @geckods In any case, I've added an adendum. – user2864740 May 22 '14 at 07:55