multidimensional arrays?

2,121 views
Skip to first unread message

Nelson Luís Dias

unread,
Jul 27, 2010, 5:13:26 PM7/27/10
to golang-nuts
First of all, very nice language, congrats.

Alas, for my everyday uses (scientific programming), it misses two
things available both in FORTRAN and in Python+Numpy [If Go will never
have the ambition to be general-purpose and able to do scientific
processing, please stop reading now and forget about this]:

a) an exponentiation operator
b) a truly multidimensional array.

In order to create a 6x6 matrix and then slice it, I have to go to the
trouble of writing two functions (see the code below: it compiles and
runs). Instead, I would like to be able to say such things as

var a [6,6]int

to create a 2-D array with contiguous storage,

a[i,j]

to access an element, and

b := a[0:3,0:3]

to slice it.
Therefore I would suggest Go two have *two* types of arrays: arrays of
arrays (already extant) and multimensional arrays (as suggested
above). Most scientific programmers will not jump in without such
ammenities. Mind you, I am no FORTRAN fan and would be eager to
change (partly) to Go if only...

Thanks anyway!

Nelson

package main
import fmt "fmt"
// ---------------------------------------------------------
// how to create and allocate a matrix
// ---------------------------------------------------------
func newmat(m, n int) [][]int {
mat := make([][]int, m)
for i := range mat {
mat[i] = make([]int, n)
}
return mat
}
// ---------------------------------------------------------
// how to slice a matrix
// ---------------------------------------------------------
func slicer( a [][]int, m,n int) [][]int {
b := make( [][]int, m)
for i := 0; i < m ; i++ {
b[i] = a[i][0:n]
}
return b
}
// ---------------------------------------------------------
// do something on a whole matrix
// ---------------------------------------------------------
func sum(a [][]int) int {
s := 0
for i := range a {
for j := range a[i] {
s += a[i][j]
}
}
return s
}
// ----------------------------------------------------------
// do stuff
// ----------------------------------------------------------
func main() {
// ----------------------------------------------------------
// make a matrix
// ----------------------------------------------------------
a := newmat(6,6)
for i := 0; i < 6 ; i++ {
for j := 0; j < 6; j++ {
a[i][j] = 10*(i+1)+(j+1)
}
}
fmt.Println("Allocated 6 x 6:")
fmt.Println(a)
fmt.Println("sums to:")
fmt.Println(sum(a))
fmt.Println("Sliced 3 x 3, b:")
b := slicer(a,3,3)
fmt.Println(b)
// -----------------------------------------------------------
// but if c is a static array and I want to slice it...
// -----------------------------------------------------------
var c [6][6]int
for i := 0; i < 6 ; i++ {
for j := 0; j < 6; j++ {
c[i][j] = 10*(i+1)+(j+1)
}
}
d := make( [][]int, 3)
for k := 0; k < 3 ; k++ {
d[k] = c[k][0:3]
}
fmt.Println("Sliced 3 x 3, d:")
fmt.Println(d)
}



Alexander Surma

unread,
Jul 27, 2010, 5:27:20 PM7/27/10
to golan...@googlegroups.com
You introduce two new problems (though under the hood).
A slice is nothing more (and nothing less) than a struct with a pointer
to an array, the capacity of that array and the current bounds
of the slice. If d something like slice[2:4], everything stays, but the
boundaries. This structure would obviously not work with
multidimensional arrays. You'd have to add expensive access checking
which is just not worth it.
Also, this introduces another type which is semantically equivalent to
an normal array/slice, but in fact is not. This can cause great confusion.
Why don't you write yourself a little multidim-array-lib which does all
that for you and work with it? (I know, that'd be no *true*
multidimensional array, but there is and probably will be no other way)

As for the exponential operator: That's just not a low level operation.

Surma

Clark Gaebel

unread,
Jul 27, 2010, 8:32:36 PM7/27/10
to golang-nuts
Couldn't you fix this by using math.Pow, and maybe adding an integer
exponentiation function to the math library (such as math.IPow)?

Cory Mainwaring

unread,
Jul 27, 2010, 9:04:19 PM7/27/10
to Clark Gaebel, golang-nuts
If you know how big your matrix is going to be just define it with: matrix := [6][6]int

David Roundy

unread,
Jul 28, 2010, 6:14:03 AM7/28/10
to Cory Mainwaring, Clark Gaebel, golang-nuts
On Tue, Jul 27, 2010 at 9:04 PM, Cory Mainwaring <olr...@gmail.com> wrote:
> If you know how big your matrix is going to be just define it with: matrix
> := [6][6]int

But it seems a real shame to go back to the old days of fortran 77,
where we need to recompile our program to solve a problem of a
different size. True, recompiling with go is fast, but then we also
need to use some sort of preprocessing to set the dimensions properly.
Alas, I am reminded of an mpi program written in fortran 77, which
used sh and factor to split the problem between nodes, since this
couldn't be done in the fortran program itself, since it determines
the size of the arrays.

David

Cory Mainwaring

unread,
Jul 28, 2010, 10:06:28 AM7/28/10
to David Roundy, Clark Gaebel, golang-nuts
obviously, this can be done with slices and the like, and as with most languages, matrix operations are a bit difficult. I'm not sure if adding syntax to make life easier would be beneficial, but it's worth a consideration (though the advent of a new type that's similar enough to be confusing, but different enough to cause damage doesn't seem so good). It would be an addition to the for loop syntax that would be of the most help here I think. We have pretty good notation for multi-dimensional access, though initializing a multi-dimensional array with make would be nice.

Norman Yarvin

unread,
Jul 31, 2010, 12:55:58 AM7/31/10
to David Roundy, Cory Mainwaring, Clark Gaebel, golang-nuts
On Wed, Jul 28, 2010 at 06:14:03AM -0400, David Roundy wrote:
>On Tue, Jul 27, 2010 at 9:04 PM, Cory Mainwaring <olr...@gmail.com> wrote:
>> If you know how big your matrix is going to be just define it with: matrix
>> := [6][6]int
>
>But it seems a real shame to go back to the old days of fortran 77,
>where we need to recompile our program to solve a problem of a
>different size.

That's putting it mildly, and a bit insultingly to Fortran 77, which
really is better at multidimensional arrays than Go is at present. In
Fortran 77, one can write a subroutine that, for instance, multiplies two
matrices which are of arbitrary dimensions (the dimensions being supplied
as parameters to the subroutine), while still using the usual Fortran
syntax for accessing matrix elements. (That is, writing "a(i,j)" to
denote the i,j'th element, rather than needing to write something like
"a(i*n+j)".) But in Go, one has to write something like "a[i*n+j]" --
or, alternatively, use slices of slices, which yields a different
implementation, and for most purposes a slower one (since pointer-chasing
is slow on modern machines).

This seems to me to be completely gratuitous: there's no reason at all
that the language couldn't support multidimensional arrays very well.
I've outlined before how that might be done (while respecting Go's
distinction between arrays and slices, which I think is an excellent one,
and not altering the meaning of any existing Go code):

http://groups.google.com/group/golang-nuts/msg/2ba08fe85b97f441

but didn't get much response. I suppose it's a question of priorities:
when you're in the business of writing multi-thousand-line programs, in
which every other line of code involves multidimensional arrays, being
able to write a[i,j] rather than a[i*n+j] is a feature to kill for. It
is also often important for such codes to be fast, which weighs against
using slices of slices. But to systems programmers, such things can seem
like mere luxuries.


--
Norman Yarvin http://yarchive.net

Ricardo Panaggio

unread,
Jul 31, 2010, 11:06:24 AM7/31/10
to Norman Yarvin, David Roundy, Cory Mainwaring, Clark Gaebel, golang-nuts

Norman Yarvin +1

Message has been deleted

Gabor

unread,
Jul 31, 2010, 12:44:05 PM7/31/10
to golang-nuts
request for multidimensional arrays: +1

It would be instructive to read a response from the developers of Go.
Are multidimensional arrays of low priority and it is only a question
of time/manpower,
or should Fortran users stay where they are, they will never be a
target audience ?


On Jul 31, 6:55 am, Norman Yarvin <yar...@yarchive.net> wrote:
> On Wed, Jul 28, 2010 at 06:14:03AM -0400, David Roundy wrote:
Message has been deleted

Cory Mainwaring

unread,
Jul 31, 2010, 2:21:49 PM7/31/10
to Gabor, golang-nuts
I'm still confused on this whole multi-dimensional array thing, why is it so bad to have slices of slices? Why is is so bad to have to declare your multi-dimensional array at compile-time? Why would a really long slice be equal to a multi-dimensional array (since that's not even an array, thus begging question number one again)?

The reasoning behind the questions: slices of slices don't inherently seem a slow or inefficient construct with proper compiler support and long slices are not even arrays so they can't be multi-dimensional arrays even as well as slices of slices. The only arguments I see are that defining arrays at runtime is nice (true), and having a [i,j] syntax is nice (true). However, the second thing also makes the language's syntax more complex, and the first makes panicking at runtime likely (finding a contiguous block of RAM 1gb big is next to impossible, without moving everything around, which the OS might do, but there's no guarantee it won't fail, or worse, silently fail).

Also, what I'm really seeing here is not people looking for multi-dimensional arrays (things that Go already supports), but rather that multi-dimensional arrays be as easy to build and use as normal arrays, while separating the length of the array from the type, so that it can be changed or defined at runtime. It all just seems a bit superfluous to me, is there a reason outside of convenience? (Multi-dimensional arrays handle static arrays, Multi-dimensional slices handle dynamic arrays)

On 31 July 2010 12:45, Gabor <g...@szfki.hu> wrote:
request for multidimensional arrays: +1

It would be instructive to read a response from the developers of Go.
Are multidimensional arrays of low priority and it is only a question
of time/manpower,
or should Fortran users stay where they are, they will never be a
target audience ?


On Jul 31, 6:55 am, Norman Yarvin <yar...@yarchive.net> wrote:
> On Wed, Jul 28, 2010 at 06:14:03AM -0400, David Roundy wrote:

Norman Yarvin

unread,
Jul 31, 2010, 6:03:26 PM7/31/10
to Cory Mainwaring, Gabor, golang-nuts
On Sat, Jul 31, 2010 at 02:21:49PM -0400, Cory Mainwaring wrote:

> Why is is so bad to have to declare your
>multi-dimensional array at compile-time?

The problem isn't that you have to declare it per se, but that you have
to decide exactly what its dimensions are at compile time. You can write
a routine to multiply two 10x10 matrices, using Go's existing arrays of
arrays; but if you want to generalize that routine so that it multiplies
two NxN matrices, you're out of luck: you have to rewrite it to use a
different sort of data structure (slices of slices), and have to change
all the callers of the routine so that they pass in such a data
structure. The situation is somewhat like what Brian Kernighan
complained about in section 2.1 of "Why Pascal is Not My Favorite
Programming Language":

http://www.lysator.liu.se/c/bwk-on-pascal.html

except here the complaint only applies to multidimensional arrays; for
one-dimensional arrays, Go's slices provide a very nice way to deal with
them. It's just that presently, there is no such thing as a
multidimensional slice. Go, in this respect, provides about the same
functionality that C did before C99, where the first dimension of an
array could vary at runtime, but subsequent dimensions had to be fixed at
compile time. You could write the function declaration:

void f(double a[][10], int n)
{
....

but not

void f(double a[n][m], int n, int m)
{
....

as you can at present. If you wanted the latter sort of functionality,
the only way to get it was to use pointers to pointers: the function
would be declared as:

void f(double **a, int n, int m)
{
....

and what would be passed in would not be a pointer to a section of memory
containing n*m doubles, but rather a pointer to a section of memory
containing n pointers, each of which pointed to one row of the array.

>The reasoning behind the questions: slices of slices don't inherently seem a

>slow or inefficient construct with proper compiler support [...]

They really are. Slices-of-slices multidimensional arrays are
implemented in much the same way as pointer-to-pointer multidimensional
arrays are in C, the main difference being that the extra array contains
not only pointers, but also capacities and lengths -- the other
attributes of a slice. This is, in most respects, a more flexible data
structure: not only can each row of the array be allocated in a place
completely unrelated to where other rows of the array are stored, but in
addition, each row can have a different length. That flexibility can be
useful... but it has a cost. It means using an entirely different memory
layout from traditional arrays, where an N by M array consists of a
continuous hunk of N*M entries. Even if, in practice, the data is always
going to be laid out in a continuous hunk, and the lengths of the rows
are always all going to be the same, the compiler generally can't know
that: instead it has to generate code for the general case -- doing
bounds checks for each row that is accessed, for instance, where if it
knew all the rows were the same length, it would only have to do the
bounds check once.

But the main cost is that of retrieving and following pointers, which is
a relatively slow operation on modern machines. Doing a multiplication
to calculate the position in memory of an array element is generally
faster than retrieving a pointer from which to calculate that position.

Also, with a continuous hunk of memory, the compiler can implement taking
a submatrix using a trivial amount of arithmetic. If, for instance, one
singles out a submatrix consisting of rows 2 through 4 of columns 3
through 6 of an N by M matrix -- what I'd write as a[2:4,3:6] -- the
compiler just implements that by adding 2*M+3 to the base pointer to the
matrix, setting the first dimension to three, the second dimension to
four, and the stride to M. With pointers to pointers (or slices of
slices), selecting the same submatrix would require allocating and
filling a new pointer array -- which is, in my opinion, too much of a
heavyweight operation for it to be good for the compiler to do it
automatically for you.

It's not entirely inconceivable to write a compiler that would look at a
program using slices of slices, figure out that it really could have been
written as operations on continuous hunks of memory, and compile it that
way, as an "optimization". But writing such a compiler would be vastly
more difficult than writing a compiler that implemented both sorts of
data structure, and just did whatever the user told it to do.


> The only arguments I see are that defining arrays at
>runtime is nice (true), and having a [i,j] syntax is nice (true). However,
>the second thing also makes the language's syntax more complex, and the
>first makes panicking at runtime likely (finding a contiguous block of RAM
>1gb big is next to impossible, without moving everything around, which the
>OS might do, but there's no guarantee it won't fail, or worse, silently
>fail).

Finding a monster-sized chunk of contiguous memory is a battle that one
faces in any language where memory is allocated and freed. But
multidimensional arrays don't necessarily have to be monster-sized.


>Also, what I'm really seeing here is not people looking for
>multi-dimensional arrays (things that Go already supports), but rather that
>multi-dimensional arrays be as easy to build and use as normal arrays, while
>separating the length of the array from the type, so that it can be changed
>or defined at runtime. It all just seems a bit superfluous to me, is there a
>reason outside of convenience?

Convenience, though often sneered-at, is a great force in this world.
It's why we don't use Turing machines to do computing with.

Cory Mainwaring

unread,
Jul 31, 2010, 6:12:38 PM7/31/10
to Norman Yarvin, Gabor, golang-nuts
Couldn't a Matrix type be built using a slice with N*M size, and i*N+j location? It wouldn't be quite as pretty as a[i,j], but it could at least be a.At(i, j). Not the best solution, but definitely a temporary solution until the if/when point of integrating this concept into the compiler itself comes to fruition.

David Roundy

unread,
Aug 1, 2010, 5:27:47 PM8/1/10
to Cory Mainwaring, Norman Yarvin, Gabor, golang-nuts
Yes, that's what's done in C or C++.

David

David/Jones

unread,
Aug 1, 2010, 7:28:32 PM8/1/10
to golang-nuts


On 1 Aug., 23:27, David Roundy <roun...@physics.oregonstate.edu>
wrote:
> Yes, that's what's done in C or C++.
>
> David
>
> On Sat, Jul 31, 2010 at 6:12 PM, Cory Mainwaring <olre...@gmail.com> wrote:
> > Couldn't a Matrix type be built using a slice with N*M size, and i*N+j
> > location? It wouldn't be quite as pretty as a[i,j], but it could at least be
> > a.At(i, j). Not the best solution, but definitely a temporary solution until
> > the if/when point of integrating this concept into the compiler itself comes
> > to fruition.

Now if we could overload the [] operator... /hopes not to start
another of these threads...
Reply all
Reply to author
Forward
0 new messages