Nooby question : multidimensional arrays

[snip]

If you need that you could do something like Multi Dimensional Array · GitHub

[snip]
Are you using a ruby newer than 1.8.7. I'm getting an error as shown here:

~/prog_dev/ruby/gist772827-61376303a967918b698bc0ddb50af26d6368438c $
ruby mda.rb
mda.rb:20: syntax error, unexpected tIDENTIFIER, expecting tAMPER or '&'
  def =(*idx, val)
                   ^
mda.rb:41: syntax error, unexpected kEND, expecting $end
~/prog_dev/ruby/gist772827-61376303a967918b698bc0ddb50af26d6368438c $
ruby -v
ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]

···

On 01/10/11 08:33, Robert Klemme wrote:

Ben Giddings wrote:

04 @data = Array.new(width) { Array.new(height) }

Create an Array named @data initially with 'width' rows. Set the
value of each element of the array to a new array with 'height' rows.

If 'width' is 3 and 'height' is 2, @data initially looks like:

[[nil, nil], [nil, nil], [nil, nil]]

I have no problem understanding this bit. An instance variable has been
created called "data". The value of this variable is unique for each
object created in this class. This variable is equal to an array inside
an array; the {} are used to pass the Array.new(height) into each part
of the Array.new(width).

07 def (x, y)
08 @data[y]
09 end

Create a method to access the elements like so:

obj[2,0] returns the value of @data[2][0] (here it would be nil)

I understand what this is doing but not how it works. As far as I can
see it's creating a class Array2D method named , with parameters x and
y. So when you call this method it should look like:

2D_array_obj = Array2D.new(3,8)
puts 2D_array_obj.(1,1) <-- should put whatever is in row 1, column 1.
puts 2D_array_obj[1,1] <-- but this is what works to display row 1,
column 1.

How can you name a method when such syntax is used so often
elsewhere? How do those 1's get inside the square brackets?

Then there is the line:

08 @data[y]

Which is actually:

08 a 2D array [y]

Is the syntax a way of accessing an array inside an array normally?
I know you can access a hash inside array using this method. I am even
using such code to substitute values into a hash inside my Array2D
object like so:
2D_array_obj[x,y]["hash_key"] = new value

11 def =(x, y, value)
12 @data[y] = value
13 end

Create a method to set the values:

obj[1,1] = 4 assigns 5 to the array element at of @data[1][1]

After which, @data would look like:

[[nil, nil], [nil, 5], [nil, nil]]

I assume "obj[1,1] = 4 assigns 5..." is a typo and should be "obj[1,1] =
5 assigns 5..."?

Here we have a different syntax with = being used. Is = the name of
the method? Now you've explained it I know what this is doing but it's
still not clear how it works.

I'm not sure what looping logic you're talking about. There are no
loops here, nor any hashes. Array access syntax looks the same as
hashes. If you think there are hashes because of the block braces
{ }, it's an unfortunate thing that they share the same syntax, but
generally whether it's a block or an array is pretty clear from the
context.

I thought because of the that it was accessing a hash. Isn't this
how you access a hash inside an array?

I'm using this code in a simple 2d array. It was working but then I
changed all of my ranges from (0...value) to (1..value) [I used ranges
to populate my 2D array with hashes] I now get the error message:

What's probably happening is that you're going beyond the initial size
of the array (width, height). If your array is 2 by 3 and you try to
do:

x[10, 20] = 5

Internally it tries to do @data[10][20] = 5. @data[10] is past the
end of the array, so its value is nil, so @data[10][20] = 5 becomes
nil[20] = 5.

Giving the error message:

11:in `=': undefined method `=' for nil:NilClass (NoMethodError)

You could probably fix that by changing the = method to something
like:

def =(x, y, value)
   # If @data is nil, create it as an empty array
   @data ||= Array.new
   @data[y] = value
end

If you do that, you should also fix to create missing values too:

def (x, y)
   # If @data is nil, create it as an empty array
   @data ||= Array.new
   @data[y]
end

I think I know what's going wrong. The size of the array hasn't changed,
but where I start entering values into the array has, which means
there's no room for the last row and column. Basically what I've done is
change (0...5), which is 0 1 2 3 4, to (1..5), which is 1 2 3 4 5 - both
the same size. But when I use this new range to iterate through my array
the first position addressed is now 1; so I'm trying to fit 5 units into
a 5 unit box without filling up the space in the box allocated to unit
1.

···

On Sep 12, 2009, at 00:06, Shawn W_ wrote:

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

Thanks for the feedback!

np

I simply thought it would be nice if you can address elements simply as:
A[i, j, k ...]

true, it'd be nice but it kinda wont work... (see next)

> uhm...i'm not sure what your trying to do with this, but what if i
> wanted to
> get the 5th element in the 4th dimension?

Wouldn't that simply be dc[0,0,0,5]?

no, that wont work. try your code in irb, with your 3x5 data cube...

irb(main):055:0* dc = DataCube.new(3, 5)
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0]
irb(main):056:0> dc[0,1,2]=44
=> 44
irb(main):057:0> puts dc[0, 1, 2]
44
=> nil
irb(main):058:0> puts dc.size
125
=> nil
irb(main):059:0> dc
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0]

thats the actual representation of the data cube in your computer. i'm not
sure that's exactly what you want, but it dosnt really look right to me.

i dunno, mabye you are right and this is just a funky way to do this, i'll
take a longer look a little later, but now PIZZA!!

hex

(yes, pizza is more important than you :P)

···

>
> i'd write the generator method something like this:
>
> while i < @n
> j = 0
> while j < @m
> @array[[i]] << 0
> j += 1
> end
> i += 1
> end
>
> (i havnt tested it, so it may not work)
>
> then you'd need to define the other accessor methods & such... but
> thats a
> start
>
> hex

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

[snip]

To illustrate, the attached produces output:

44
12
[0,0,0]
0
[1,0,0]
1
[0,1,0]
2
[1,1,0]
3
[1,1,2]
11
[ [ [0, 1]
  , [2, 3]
  ]
, [ [4, 5]
  , [6, 7]
  ]
, [ [8, 9]
  , [44, 11]
  ]
]

The last several lines (starting with [ [ [)
show the output to the to_s and were designed
to show (hopefully) the nested subarray's.

-regards,
Larry

ArrayEv.rb (1.95 KB)

···

On 01/08/11 13:33, Larry Evans wrote:

On 01/07/11 21:57, Larry Evans wrote:

On 01/07/11 17:56, Kedar Mhaswade wrote:

Is this an okay job of implementing a multidimensional array?

datacube/data_cube.rb at master · kedarmhaswade/datacube · GitHub

Thank you for any feedback.

-Kedar

There's a book:

  An APL Compiler

which describes an expansion vector which is a scan of
the array sizes. IOW, for:

  arr = Array.new(s0,s1,...sn)

the expansion vector for this arr is:

  arr.ev = [1, s0, s0*s1, s0*s1*s3,..., s0*s1*...*sn]

The expansion vector for DataCube.new(n,m) would be:

  [m**0,m**1,m**2,...,m**n]

IOW, the length of this expansion vector is n+1.

[snip]

    arr[i0,i1,..., ij, ... in]

is located:

    i0*arr.ev[0]+i1*arr.ev[1]+...+in*arr.ev[n]

elements from the 1st element:

    arr[0,0,...,0]

Thus, given the sizes, you can create a member variable
which is the expansion vector, then use that to access the
elements by calculating the offset from the initial element
using the dot product:

    i0*arr.ev[0]+i1*arr.ev[1]+...+in*arr.ev[n]

If you need that you could do something like
Multi Dimensional Array · GitHub

Interesting. Thanks, Robert.
But using hash internally to represent arrays is slightly
counterintuitive.

Well, you don't see it from the outside. For users of the class it
doesn't really matter how it internally works (unless they hit some
limitations).

But I agree, it will work well for "sparse"
multi-dimensional arrays.

Exactly that was the use case I had in mind.

I realize that I could do something similar
and still retain the one-dimensional flattening, in my implementation.

How exactly do you want to do that?

And yes, expanding it to do non-cube entities is on my list.

:slight_smile: Actually "cube" is just a special case of the more general one.
I'd rather implement the special case and add a custom constructor for
the cube case:

Cheers

robert

···

On Mon, Jan 10, 2011 at 4:14 PM, Kedar Mhaswade <kedar.mhaswade@gmail.com> wrote:

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

Yes, you need 1.9ish Ruby for this to work. Reason is that 1.9 has
extended pattern matching. With 1.8 you need to do it yourself:

09:14:49 ~$ irb
Ruby version 1.8.7
irb(main):001:0> class X
irb(main):002:1> def =(*a)
irb(main):003:2> v = a.pop
irb(main):004:2> p a, v
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> X.new[1,2,3]=4
[1, 2, 3]
4
=> 4
irb(main):008:0>

Sorry for not mentioning it.

Kind regards

robert

···

On Mon, Jan 10, 2011 at 9:15 PM, Larry Evans <cppljevans@suddenlink.net> wrote:

On 01/10/11 08:33, Robert Klemme wrote:
[snip]

If you need that you could do something like Multi Dimensional Array · GitHub

[snip]
Are you using a ruby newer than 1.8.7. I'm getting an error as shown here:

~/prog_dev/ruby/gist772827-61376303a967918b698bc0ddb50af26d6368438c $
ruby mda.rb
mda.rb:20: syntax error, unexpected tIDENTIFIER, expecting tAMPER or '&'
def =(*idx, val)
^
mda.rb:41: syntax error, unexpected kEND, expecting $end
~/prog_dev/ruby/gist772827-61376303a967918b698bc0ddb50af26d6368438c $
ruby -v
ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]

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

A better way to put it:

was:
[0,1,2,3,4]

now:
[nil,1,2,3,4]5

···

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

Hi --

Ben Giddings wrote:

04 @data = Array.new(width) { Array.new(height) }

Create an Array named @data initially with 'width' rows. Set the
value of each element of the array to a new array with 'height' rows.

If 'width' is 3 and 'height' is 2, @data initially looks like:

[[nil, nil], [nil, nil], [nil, nil]]

I have no problem understanding this bit. An instance variable has been
created called "data". The value of this variable is unique for each
object created in this class. This variable is equal to an array inside
an array; the {} are used to pass the Array.new(height) into each part
of the Array.new(width).

07 def (x, y)
08 @data[y]
09 end

Create a method to access the elements like so:

obj[2,0] returns the value of @data[2][0] (here it would be nil)

I understand what this is doing but not how it works. As far as I can
see it's creating a class Array2D method named , with parameters x and
y. So when you call this method it should look like:

2D_array_obj = Array2D.new(3,8)
puts 2D_array_obj.(1,1) <-- should put whatever is in row 1, column 1.
puts 2D_array_obj[1,1] <-- but this is what works to display row 1,
column 1.

How can you name a method when such syntax is used so often
elsewhere? How do those 1's get inside the square brackets?

You can name it because the parser knows what it means in a given
context. This:

   identifier

always means the method.

As for the 1's inside the brackets: that's syntactic sugar. When Ruby
sees this:

   x[y]

it automatically converts it to:

   x.(y)

Then there is the line:

08 @data[y]

Which is actually:

08 a 2D array [y]

Is the syntax a way of accessing an array inside an array normally?

It really means this:

   @data.(x).(y)

In other words, it's calling the method first on @data, then on the
return value of @data.

I know you can access a hash inside array using this method. I am even
using such code to substitute values into a hash inside my Array2D
object like so:
2D_array_obj[x,y]["hash_key"] = new value

11 def =(x, y, value)
12 @data[y] = value
13 end

Create a method to set the values:

obj[1,1] = 4 assigns 5 to the array element at of @data[1][1]

After which, @data would look like:

[[nil, nil], [nil, 5], [nil, nil]]

I assume "obj[1,1] = 4 assigns 5..." is a typo and should be "obj[1,1] =
5 assigns 5..."?

Here we have a different syntax with = being used. Is = the name of
the method? Now you've explained it I know what this is doing but it's
still not clear how it works.

Yes, = is the name of the method.

I thought because of the that it was accessing a hash. Isn't this
how you access a hash inside an array?

It's all about method calls. by itself doesn't mean array or hash
or any other class of object. If you do this:

   x[y][z]

then you're calling the method on x, with the argument y -- which
can return an array, a hash, a string, a Person instance, anything at
all -- and then you're calling on whatever you got back, with the
argument z.

David

···

On Sat, 12 Sep 2009, Shawn W_ wrote:

On Sep 12, 2009, at 00:06, Shawn W_ wrote:

--
David A. Black, Director
Ruby Power and Light, LLC (http://www.rubypal.com)
Ruby/Rails training, consulting, mentoring, code review
Book: The Well-Grounded Rubyist (http://www.manning.com/black2\)

alright, so i've played around with it a bit more, and i kind of like your
implementation... though if your trying to do what i think you are there are
a few bugs in the implementation you should fix.

from what i can see you want someone to be able to use a call like: dc[0,1]
and get the second element of the second dimension of the data cube right?
i think it's kinda cool, but the way it's currently implemented calling
dc[3] does the same thing. in fact, check out this little irb session i
just did:

irb(main):100:0> dc = DataCube.new(1, 1)
=> [0]
irb(main):101:0> dc[1,1]=1
=> 1
irb(main):102:0> dc
=> [0, nil, nil, 1]
irb(main):103:0> dc[1,0]=2
=> 2
irb(main):104:0> dc
=> [0, 2, nil, 1]
irb(main):105:0> dc[3]=7
=> 7
irb(main):106:0> dc
=> [0, 2, nil, 7]

i dont think that's the behavior you want. that aside, if you can get it
working the way you want it to i think it'd be a pretty cool way to access
everything.

though how would i get all the elements in dimension 3? reading up on data
cubes real quick one may want to do that... but overall it looks good and
your implementation - though to my best knowledge unorthodox - will probably
work fine if done right.

hex

(oh, and pizza rox sox!!)

···

On Fri, Jan 7, 2011 at 8:27 PM, serialhex <serialhex@gmail.com> wrote:

Thanks for the feedback!
>

np

>
> I simply thought it would be nice if you can address elements simply as:
> A[i, j, k ...]
>

true, it'd be nice but it kinda wont work... (see next)

>
> > uhm...i'm not sure what your trying to do with this, but what if i
> > wanted to
> > get the 5th element in the 4th dimension?
>
> Wouldn't that simply be dc[0,0,0,5]?
>

no, that wont work. try your code in irb, with your 3x5 data cube...

irb(main):055:0* dc = DataCube.new(3, 5)
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0]
irb(main):056:0> dc[0,1,2]=44
=> 44
irb(main):057:0> puts dc[0, 1, 2]
44
=> nil
irb(main):058:0> puts dc.size
125
=> nil
irb(main):059:0> dc
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0,
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0]

thats the actual representation of the data cube in your computer. i'm not
sure that's exactly what you want, but it dosnt really look right to me.

i dunno, mabye you are right and this is just a funky way to do this, i'll
take a longer look a little later, but now PIZZA!!

hex

(yes, pizza is more important than you :P)

>
> >
> > i'd write the generator method something like this:
> >
> > while i < @n
> > j = 0
> > while j < @m
> > @array[[i]] << 0
> > j += 1
> > end
> > i += 1
> > end
> >
> > (i havnt tested it, so it may not work)
> >
> > then you'd need to define the other accessor methods & such... but
> > thats a
> > start
> >
> > hex
>
> --
> Posted via http://www.ruby-forum.com/\.
>
>

Robert Klemme wrote in post #973678:

If you need that you could do something like
Multi Dimensional Array · GitHub

Interesting. Thanks, Robert.
But using hash internally to represent arrays is slightly
counterintuitive.

Well, you don't see it from the outside. For users of the class it
doesn't really matter how it internally works (unless they hit some
limitations).

I meant for a programmer to implement it. And yes, it is a damn good
idea.

But I agree, it will work well for "sparse"
multi-dimensional arrays.

Exactly that was the use case I had in mind.

I realize that I could do something similar
and still retain the one-dimensional flattening, in my implementation.

How exactly do you want to do that?

Grrr. I was not thinking enough. I could make the current implementation
better in that I can lazily expand the array which will fill the
intervening elements with nil's but yeah, it's nowhere near the fast
lookup of hashtables.

···

On Mon, Jan 10, 2011 at 4:14 PM, Kedar Mhaswade > <kedar.mhaswade@gmail.com> wrote:

And yes, expanding it to do non-cube entities is on my list.

:slight_smile: Actually "cube" is just a special case of the more general one.
I'd rather implement the special case and add a custom constructor for
the cube case:

Multi Dimensional Array · GitHub

Cheers

robert

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

Thank you for your feedback, again!

I simply started with flattening out the elements into a one-dimensional
array internally (which is quite obvious). I am not sure about its
applications yet. Also, dc[3] does not make sense for this
implementation, unless the DataCube itself is one-dimensional (in which
case it just degenerates to normal array).

So, if you have p dimensions, then the array has to be accessed (or
indexed) using an index like: [x, y, z, ...] where the length of this
array itself is p.

That said, I haven't yet considering "expanding the array" dynamically.
It's like, you fix the # and size of dimensions upfront and freeze them.
I will address that officially, as you suggest.

I will admit, I haven't checked the current literature of Data Cube when
implementing it. Maybe I should do that.

My modest expectation was that to address a point in multidimensional
space A[d1,d2,d3 ... dn] was a more natural notation than
A[d1][d2][d3]...[dn], because the former is how we would represent it on
paper. And Ruby's flexibility allowed me to do just that!

Regards,
Kedar

···

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