View mode: basic / threaded / horizontal-split · Log in · Help
December 26, 2006
Re: DMD 0.177 release [Length in slice expressions]
== 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
Re: DMD 0.177 release [Length in slice expressions]
== 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
Re: DMD 0.177 release
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
Re: DMD 0.177 release [Length in slice expressions]
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
Next ›   Last »
18 19 20 21 22
Top | Discussion index | About this forum | D home