December 26, 2006
== Quote from Bill Baxter (dnewsgroup@billbaxter.com)'s article
> Oskar Linde wrote:
>> Bill Baxter wrote:
>>> After trying to write a multi-dimensional array class, my opinion is that D slice support could use some upgrades overall.
>>
>> I'd be very interested in looking at what you've come up with. With my own implementation of a multi-dimensional array type a couple of months ago, I came to the same conclusion. I posted about it in:
>>
>> news://news.digitalmars.com:119/edrv0n$hth$1@digitaldaemon.com http://www.digitalmars.com/d/archives/digitalmars/D/announce/4717.html
>>
>>> What I'd like to see:
>>>
>> --MultiRange Slice--
>>> * A way to have multiple ranges in a slice, and a mix slice of and
>>> non-slice indices:
>>>     A[i..j, k..m]
>>>     A[i..j, p, k..m]
>> (snip)
>>>     A[0..$,3..$]
>>
>> Yes, I would too. It is quite frustrating having the syntax in the language but not being allowed to utilize it... :)
>>
>> I work around this by instead using a custom slice syntax instead:
>>
>> A[range(i,j), range(k,m)]
>> A[range(i,j), p, range(k,m)]
>> A[range(0,end), range(3..end)]
>> A[end-1, p % end]
> Yeh, that's similar to what I'm doing too.  But it's pretty ugly.  So I guess that means you're using opIndex for everything and leaving opSlice alone.

Yes. Apart from the no-argument opSlice and opSliceAssign, that I overload with the same meaning as for the built in arrays. opSlice is not designed to support more than one dimension.

> Are you able to have ranges return arrays and specific indexes return scalar values that way?

Yes.

> That seems to me a big reason for having
> opSlice exist in the first place.  The .. in the brackets not only means
> you're slicing, it also means the function should return another array,
> versus returning an element.  That seems like a nice distinction to have
> to me.

Yes, definitely. My implementation mimics the behavior of the D native array/slice type (T[]), but extended for multiple dimensions and strides.

So given Array!(2,int) A,  A[all,5] and A[5,all] are both Array!(1,int) of the 6-th row and column of A. A[5,5] is an int. Some other neat features made possible by strided arrays are for instance A.diag, that returns the 1-dimensional diagonal of the array, so eg:

A[] = 0;
A.diag[] = 1;

would construct the unit matrix.

>> Basicly, the transformation is:
>>
>> $ => end
>> a..b => range(a,b)
>>
>> I briefly described this in: news://news.digitalmars.com:119/eft9id$2aq3$1@digitaldaemon.com
> Thanks for the link.  The 'end' thing isn't so bad, at least for a former Matlab user. :-)

I wish I too could call myself a _former_ such. :)

/Oskar
December 26, 2006
== Quote from Reiner Pope (xxxxxx@xxx.xx)'s article
> Is there anything particularly wrong with having foo[a..b,c,d..$] being syntactical sugar for foo[a..b][c][d..$] ? That way, I would imagine you could implement slicing and indexing of multidimensional arrays quite easily because, as Norbert Nemec said, indexing just returns an array with dimension reduced by one, and slicing returns an array of the same dimension, but perhaps different size. It also seems to allow any combination of slicing and indexing without needing variadic functions.

It is a neat idea that unfortunately doesn't work.

foo[a..b,c..d] means slice from a to b along dimension 1 and from c to d along dimension 2. foo[a..b] would therefore return a 2-dimensional slice (same dimensionality as foo), and (foo[a..b])[c..d] would slice the same dimension again. Unless, of course, foo[a..b] would return some kind of proxy object that kept track of the last sliced dimension.

/Oskar
December 26, 2006
Ohh, why didn't you start a new thread... I hate it when a thread gets so nested that one needs a widescreen monitor to view the topic list. :P

Andrei Alexandrescu (See Website for Email) wrote:
> Kevin Bealer wrote:
>> == Quote from Andrei Alexandrescu (See Website For Email)
>> ...
>>> That remains to be seen, but I think the buck stops at functions. The
>>> problem of duplicating templates for inout (lvalues) and rvalues
>>> stays, but I have an idea about that, that I might tell about
>>> tomorrow.
>>>
>>> Andrei
>>
>> Did you ever work out how to do this lvalue/rvalue idea?
> 
> I had to leave to Romania before having the time to post thoughts on the lvalue/rvalue discussion I've had with Walter on Saturday. We both agree that it's a serious problem with D's type system that needs fixing (and that he needs to do all the work :o)). I've continued to think of the issue, at least enough to figure that the solution we initially thought of is not sound.
> 
> Walter is reluctant to offering the ability to overload on lvalue/rvalue, while it turns out that that can't be avoided. Let's return to my litmus test - the identity function ident(e), which can snuggle any expression e and leave its semantics unchanged. My thesis is that this function is an important test of a language's power. The starting point would be:
> 
> template ident(T) {
>   T ident(T e) { return e; }
>   inout T ident(inout T e) { return e; }
> }
> 
> The problem with this approach is that it doesn't scale. Right now "inout" is about the only interesting storage class of a function parameter, but "lazy" comes to mind (which I hope to get rid of soon via a much better solution) and later on we'll have "const", and each of these combinations will mean one more duplication of the ident body (and, by extension, of any function that wants to just pass the storage class outside). Walter had an idea along the line:
> 

Hum, I wonder if the 'lazy' "storage class" (it's not really a storage class per se) is something that is worth, or even makes sense preserving. If you want to completely preserve an expression, then what are the properties/characteristics of that expression? The type is one property of course, then there is if the expression is an rvalue or lvalue, and also if it is const/final/readonly or something like that. But 'lazy' is not something that is a property/characteristic of an expression is it? It is a concept that exists only in parameter passing of function calls. What would be an ident function trying to "preserve" the lazyness of an expression?..

> template ident(T) {
>   return T ident(return T e) { return e; }
> }
> 
> which allows you to reuse the return keyword as a symbolic placeholder for passing out the storage class. This solution is severely shortsighted in that it fixes ident and only ident, whereas the purpose of ident is to serve as a simplified case for functions with multiple parameters. So this fell as well.
> 
> We then discussed another solution that I won't bore you with, as it's so wrong it hurts. My current thoughts navigate around two possible solutions. One is to make the storage part of the template parameters:
> 
> template ident(S T) {
>   S T ident(S T e) { return e; }
> }
> 
> When two adjacent symbols appear in a template parameter list, they unambiguously denote a storage class followed by a type. So "S" can bind to things like "in", "inout" etc., while "T" can bind to types. In the example above, the compiler will deduce both S and T from the argument type. It already does that, so that's no extra difficulty. The key point that makes this scale is that you can bind S and T multiple times in a variadic template. Another interesting detail is that it clarifies that you can't solve the problem without somehow compiling two versions of the ident function. So in the end overloading on "inout" is a must.
> 
> Another solution that works is to commit to the idea of associating the storage class with the parameter (and divorce it from the type entirely). In that case, the following syntax describes what happens:
> 
> template ident(T) {
>   storageof(e) T ident(storageof(e) T e) { return e; }
> }
> 
> The storageof(symbol) meta-operator yields the storage of that symbol. The problem with this notation is that it uses a symbol without having seen it. That's not too bad (it already happens due to the way symbols at global scope are looked up) but in this case it does have a fishy smell. Another thing that I don't like it that the code obscures what's going on - namely that one ident will be generated for each storage class, even though that's not reflected in the parameter type list.
> 
> Finally, one related but slightly different topic is the necessity of deduced return types for functions, e.g. by using "auto" to denote the return type. Automatic deduction of return types is very useful in that it allows compact template function definition - no more need for a template that defines a homonym function. With deduced argument types, ident can be written as:
> 
> auto ident(S T)(S T e) {
>   return e;
> }
> 
> which is, I think, the Platonic ideal of ident as far as expressing it in D goes.
> 
> 
> Andrei

Hum, interesting. I've seen this issue come up in another context as well, namely a paper about a proposal (Javari) for the addition of a reference immutability construct to Java, in the form of a 'const'-like keyword called 'readonly'. Since 'readonly' is very like (the future) 'inout', as they are both "type modifiers" of sorts, with similar syntax, they too had the issue of trying to avoid function definition duplication where only the absence or presence of 'readonly' changed in the prototype.
Their solution was similar to the above. But instead of a "storage class" modifier variable like S, they used a keyword ('romaybe'), so that a templated function would be defined as this:

  romaybe Object getValue(romaybe thisojb) {
    return thisojb.value;
  }

Which would generate two function instances, one where 'romaybe' is substituted by 'readonly' and other where it is substituted with nothing (which is mutability). The difference between the "storage class" modifier variable syntax is that only one "storage class" [*] can be parameterized, i.e., you can't have S1, S2, etc.

[*] we really should not be calling this, "storage class".

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
December 26, 2006
Oskar Linde wrote:
> == Quote from Bill Baxter (dnewsgroup@billbaxter.com)'s article
>> Oskar Linde wrote:
>>> Bill Baxter wrote:
>>>> After trying to write a multi-dimensional array class, my opinion is
>>>> that D slice support could use some upgrades overall.
>>> I'd be very interested in looking at what you've come up with. With my
>>> own implementation of a multi-dimensional array type a couple of months
>>> ago, I came to the same conclusion. I posted about it in:
>>>
>>> news://news.digitalmars.com:119/edrv0n$hth$1@digitaldaemon.com
>>> http://www.digitalmars.com/d/archives/digitalmars/D/announce/4717.html
>>>
>>>> What I'd like to see:
>>>>
>>> --MultiRange Slice--
>>>> * A way to have multiple ranges in a slice, and a mix slice of and
>>>> non-slice indices:
>>>>     A[i..j, k..m]
>>>>     A[i..j, p, k..m]
>>> (snip)
>>>>     A[0..$,3..$]
>>> Yes, I would too. It is quite frustrating having the syntax in the
>>> language but not being allowed to utilize it... :)
>>>
>>> I work around this by instead using a custom slice syntax instead:
>>>
>>> A[range(i,j), range(k,m)]
>>> A[range(i,j), p, range(k,m)]
>>> A[range(0,end), range(3..end)]
>>> A[end-1, p % end]
>> Yeh, that's similar to what I'm doing too.  But it's pretty ugly.  So I
>> guess that means you're using opIndex for everything and leaving opSlice
>> alone.
> 
> Yes. Apart from the no-argument opSlice and opSliceAssign, that I overload
> with the same meaning as for the built in arrays. opSlice is not designed
> to support more than one dimension.
> 
>> Are you able to have ranges return arrays and specific indexes
>> return scalar values that way?
> 
> Yes.
> 
>> That seems to me a big reason for having
>> opSlice exist in the first place.  The .. in the brackets not only means
>> you're slicing, it also means the function should return another array,
>> versus returning an element.  That seems like a nice distinction to have
>> to me.
> 
> Yes, definitely. My implementation mimics the behavior of the D native
> array/slice type (T[]), but extended for multiple dimensions and strides.
> 
> So given Array!(2,int) A,  A[all,5] and A[5,all] are both Array!(1,int)
> of the 6-th row and column of A. A[5,5] is an int. Some other neat
> features made possible by strided arrays are for instance A.diag, that
> returns the 1-dimensional diagonal of the array, so eg:
> 
> A[] = 0;
> A.diag[] = 1;
> 
> would construct the unit matrix.
> 

Sounds great.  Is your code available anywhere?

--bb
12 13 14 15 16 17 18 19 20 21 22
Next ›   Last »