IronLanguages / ironpython2

Implementation of the Python programming language for .NET Framework; built on top of the Dynamic Language Runtime (DLR).
http://ironpython.net
Apache License 2.0
1.07k stars 229 forks source link

Improved syntax for initializing multidimensional arrays #74

Open slide opened 7 years ago

slide commented 7 years ago

From @glopesdev on January 11, 2017 22:28

Currently, initializing a multidimensional array requires something like:

m = Array.CreateInstance(int, 3, 3)
m[0,0] = 1
m[0,1] = 2
...

This does not feel very idiomatic compared to either Python or C#, where there are much better multidimensional array initializers. Indeed:

Python/NumPy:

m = np.array([[0,  1,  2],
              [3,  4,  5],
              [6,  7,  8]])

C#:

var m = new[,]
{
    {0, 1, 2},
    {3, 4, 5},
    {6, 7, 8}
};

Besides being more concise, there are three other major nice features in the idiomatic versions:

  1. They don't require specifying the array dimensions.
  2. They don't require specifying the array type.
  3. They allow nice formatting of the values into a rectangular layout.

It seems a pity that IronPython does not provide a similarly idiomatic syntax for initializing managed multidimensional arrays. Interestingly, for 1D arrays there is in fact an "idiomatic" initializer:

m = Array[int]([0, 1, 2])

While it is still necessary to specify the array type, this feels much closer to the numpy idiom above. What would be good candidates for extending this idiom for multidimensional arrays?

In order to begin the brainstorming, I review a couple of "naive" approaches:

  1. Hijack generic type specification for specifying number of dimensions:
    m = Array[int,int]([[0, 1, 2],
                    [3, 4, 5],
                    [6, 7, 8]])

In terms of idiom, it "feels" like the current Array initializer, although of course the repetition of the type is entirely redundant (all array dimensions need to have the same type).

  1. Overload of CreateInstance or new Create method:
    m = Array.CreateInstance(int, [[0, 1, 2],
                               [3, 4, 5],
                               [6, 7, 8]])

This introduces an overload into the CreateInstance method that accepts a python list and creates an array. I understand Array is a CLR type that probably cannot be overloaded.

  1. Of couse the "simplest" alternative would be to use the generic IronPython Array class like so:
m = Array[int]([[0, 1, 2],
                [3, 4, 5],
                [6, 7, 8]])

Of course, this raises questions about how to interpret the list of lists, because you might want to create an array of lists, and not a multidimensional array. I imagine this dilemma was probably why this option was dropped originally.

However, I submit that this last objection is actually not very relevant. The reason is that the multidimensional array is a purely "managed" construct, and as such it makes sense almost exclusively for pure CLR types. The python list is a bit of a second-class citizen in IronPython anyway, because the bridge to a typed .NET list is non-transparent.

I believe it would be much more useful to actually in this case interpret the python list of python lists as a rectangular array initializer. Unfortunately, there is probably the risk that too much code would be broken by this change (maybe for IronPython 3).

Anyway, just wanted to put this out there, as I see it as one of the few sore thumbs in an otherwise very nice IronPython syntax.

Copied from original issue: IronLanguages/main#1562

0aps commented 5 years ago

This will be very useful indeed