New to Ruby: copying arrays?

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.
:slight_smile: 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.
:frowning:

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?

···

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

Alle Sunday 28 December 2008, John Park 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 Park <jcpark@soulmutation.com> (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.
:slight_smile: 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

Stefano Crocco 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! :slight_smile:

···

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

Stefano Crocco wrote:

b = Marshal.load(Marshal.dump(a))

A clever compiler would optimise that to

  b = a

(But I don't suppose Ruby does!)

Dave

···

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

Alle Sunday 28 December 2008, John Park ha scritto:

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

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:

   Pragmatic Bookshelf: By Developers, For Developers

James Edward Gray II

···

On Dec 28, 2008, at 5:02 AM, Stefano Crocco wrote:

That would be a broken compiler.

  robert

···

On 28.12.2008 13:02, Dave Bass wrote:

Stefano Crocco wrote:

b = Marshal.load(Marshal.dump(a))

A clever compiler would optimise that to

  b = a

--
remember.guy do |as, often| as.you_can - without end

John Park:

Stefano Crocco 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. :slight_smile:

-- Shot

···

--
FORTRAN was the language of choice for the same reason
that three-legged races are popular. -- Ken Thompson