Forum: Ruby New to Ruby: copying arrays?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
John P. (Guest)
on 2008-12-28 12:51
Hi.  First-time poster here.  I've done some programming in Delphi
before (like 10 years ago), and I wanted to get back to programming as a
hobby.  I've looked at several languages, and Ruby looked the funnest.
:)  So here I am.

For my first project, I've decided to make a Sudoku class, making it
general enough so that it can easily be subclassed to implement many
Sudoku variations out there.

To store the board state, I decided to make an instance variable @board
which is a two-dimensional array of 9x9.  And there would be several
methods that will manipulate the board.

Then it occurred to me that it would be nice to have a .revert method
that will revert the board to its initial state.

Easy enough, I thought.

To the .initialize method, I added the line:

@initialboard = @board

and I added a new .revert method which did:

@board = @initialboard

And this, as you Ruby-experts may imagine, is where I came to
face-to-face with Ruby's "variables are references, not containers"
nature.  Every time I manipulated @board, the "content" of @initialboard
would get altered as well, making it useless as a backup of the initial
state of the board.

I've tried .dup and .clone methods, but it didn't work because @board
was a two-dimensional, array-within-array structure.  .dup and .clone
methods only duplicated the top level array, and the sub-level arrays
were still being referenced to the same data, leading to the same result
as the first time around.

So here's my question.  Is there a simple and elegant way to duplicate
the entire content of a multi-dimensional array?

Right now, the only workarounds I can think of are: 1) store the data in
one long one-dimensional array, of 2) use an iterator to duplicate each
sub-level arrays individually.  Neither are very elegant nor attractive.
:(

Thanks in advance to any insight you guys can give me in this matter.



Oh, one more question: what's the difference between .dup and .clone
methods?
Stefano C. (Guest)
on 2008-12-28 13:03
(Received via mailing list)
Alle Sunday 28 December 2008, John P. ha scritto:
> So here's my question.  Is there a simple and elegant way to duplicate
> the entire content of a multi-dimensional array?

You can use Marshal.dump and Marshal.load:

a = [["a", "b"],["c", "d"]]
b = Marshal.load(Marshal.dump(a))
a[0][0].upcase!
puts a[0][0]  #=> A
puts b[0][0]  #=> a

Note that this only works if your array only contains serializable
objects (in
particular, it should not contain method or proc objects, IO objects and
singleton objects). See the documentation for the Marshal module for
more
information (using the command ri marshal).

>Oh, one more question: what's the difference between .dup and .clone
>methods?

As far as I know, the only difference is that clone also copies the
frozen
state of the original object, while dup doesn't:
s1 = "test"
s1.freeze
s2 = s1.dup
s3 = s1.clone
puts s2.frozen? #=> false
puts s3.frozen? #=> true

I hope this helps

Stefano
John P. (Guest)
on 2008-12-28 13:20
Stefano C. wrote:
> You can use Marshal.dump and Marshal.load:
>
> a = [["a", "b"],["c", "d"]]
> b = Marshal.load(Marshal.dump(a))
> a[0][0].upcase!
> puts a[0][0]  #=> A
> puts b[0][0]  #=> a
>

Just experimented with it in irb.  Awesome!  Exactly what I was hoping
for!

Thanks for the super-quick response!  :)
Dave B. (Guest)
on 2008-12-28 14:02
Stefano C. wrote:
> b = Marshal.load(Marshal.dump(a))

A clever compiler would optimise that to

  b = a

(But I don't suppose Ruby does!)

Dave
Robert K. (Guest)
on 2008-12-28 14:15
(Received via mailing list)
On 28.12.2008 13:02, Dave B. wrote:
> Stefano C. wrote:
>> b = Marshal.load(Marshal.dump(a))
>
> A clever compiler would optimise that to
>
>   b = a

That would be a broken compiler.

  robert
Simon K. (Guest)
on 2008-12-28 17:35
(Received via mailing list)
* John P. <removed_email_address@domain.invalid> (11:50) schrieb:

> Hi.  First-time poster here.  I've done some programming in Delphi
> before (like 10 years ago), and I wanted to get back to programming as a
> hobby.  I've looked at several languages, and Ruby looked the funnest.
> :)  So here I am.

Cool.

> To store the board state, I decided to make an instance variable @board
> which is a two-dimensional array of 9x9.  And there would be several
> methods that will manipulate the board.

Why do you want to do that? A standard Sudoku consists of 81 cells that
ordered in three different ways. Why prefer one order by your picking of
coordinates?

> Right now, the only workarounds I can think of are: 1) store the data in
> one long one-dimensional array,

That's what I did.

mfg,                       simon .... l
James G. (Guest)
on 2008-12-28 19:26
(Received via mailing list)
On Dec 28, 2008, at 5:02 AM, Stefano C. wrote:

> s3 = s1.clone
> puts s2.frozen? #=> false
> puts s3.frozen? #=> true

clone() also copies an object's singleton class:

   >> s = "cat"
   => "cat"
   >> def s.speak; "purr" end
   => nil
   >> s.speak
   => "purr"
   >> s.dup.speak
   NoMethodError: undefined method `speak' for "cat":String
     from (irb):4
   >> s.clone.speak
   => "purr"

I learned this and other great things from these screencasts:

   http://www.pragprog.com/screencasts/v-dtrubyom/the...

James Edward G. II
Shot (Piotr S.) (Guest)
on 2008-12-28 23:27
(Received via mailing list)
John P.:

> Stefano C. wrote:

>> You can use Marshal.dump and Marshal.load:

>> a = [["a", "b"],["c", "d"]]
>> b = Marshal.load(Marshal.dump(a))
>> a[0][0].upcase!
>> puts a[0][0]  #=> A
>> puts b[0][0]  #=> a

> Just experimented with it in irb.
> Awesome! Exactly what I was hoping for!

To extend on the above, what I would do would be to create a separate
Board class to represent your Sudoku board, rather than directly using
Array for this.

First, you can then implement it in various ways, and (as long as you
maintain the same methods to access and opearte on it) it would be
transparent to the Sudoku solver whether your Board is implemented
as an Array of three three-element Arrays, a nine-element ‘flat’
Array, or anything else (say, maybe 27 Sets containing 81 elements
in non-disjoint groups of nines?).

Second, you can then have

class Board
  def dup
    Marshal.load Marshal.dump(self)
  end
end

(which is an idiom I use extensively in my code) and simply use

@initialboard = @board.dup
…
@board = @initialboard.dup

in your solver. :)

-- Shot
This topic is locked and can not be replied to.