www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - opSlice and $

reply Russell Lewis <webmaster villagersonline.com> writes:
It appears to me, looking at the spec, that there isn't a way to support 
the use of $ in operator overloading.  Is there anything I've 
overlooked?  What I'm looking for, of course, is to be able to do:

class MyClass {....}

MyClass c = new MyClass;
auto x = c[1..$];
Sep 11 2007
next sibling parent reply Sean Kelly <sean f4.ca> writes:
Russell Lewis wrote:
 It appears to me, looking at the spec, that there isn't a way to support 
 the use of $ in operator overloading.  Is there anything I've 
 overlooked?  What I'm looking for, of course, is to be able to do:
 
 class MyClass {....}
 
 MyClass c = new MyClass;
 auto x = c[1..$];
The '$' symbol should map to "size_t length()" when used with opSlice, but at the moment I don't think this is the case. Sean
Sep 11 2007
parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Sean Kelly wrote:
 Russell Lewis wrote:
 It appears to me, looking at the spec, that there isn't a way to 
 support the use of $ in operator overloading.  Is there anything I've 
 overlooked?  What I'm looking for, of course, is to be able to do:

 class MyClass {....}

 MyClass c = new MyClass;
 auto x = c[1..$];
The '$' symbol should map to "size_t length()" when used with opSlice, but at the moment I don't think this is the case.
The problem how that generalizes to multidimensional slicing: c[1..$, 2..$] -- Oskar
Sep 11 2007
next sibling parent Sean Kelly <sean f4.ca> writes:
Oskar Linde wrote:
 Sean Kelly wrote:
 Russell Lewis wrote:
 It appears to me, looking at the spec, that there isn't a way to 
 support the use of $ in operator overloading.  Is there anything I've 
 overlooked?  What I'm looking for, of course, is to be able to do:

 class MyClass {....}

 MyClass c = new MyClass;
 auto x = c[1..$];
The '$' symbol should map to "size_t length()" when used with opSlice, but at the moment I don't think this is the case.
The problem how that generalizes to multidimensional slicing: c[1..$, 2..$]
Ugh. I guess we'd need an opLength after all, huh? Sean
Sep 11 2007
prev sibling next sibling parent reply Witold Baryluk <baryluk smp.if.uj.edu.pl> writes:
Dnia Tue, 11 Sep 2007 21:27:44 +0200
Oskar Linde <oskar.lindeREM OVEgmail.com> napisa=B3/a:

 Sean Kelly wrote:
 Russell Lewis wrote:
 It appears to me, looking at the spec, that there isn't a way to=20
 support the use of $ in operator overloading.  Is there anything
 I've overlooked?  What I'm looking for, of course, is to be able
 to do:

 class MyClass {....}

 MyClass c =3D new MyClass;
 auto x =3D c[1..$];
=20 The '$' symbol should map to "size_t length()" when used with opSlice, but at the moment I don't think this is the case.
=20 The problem how that generalizes to multidimensional slicing: c[1..$, 2..$]
Yes. It will be nice to have this. Currenly I use temporary class for remebering first dimension of slice, and object from which we slice. c[1..3][1..5] It is also problem that you can only have size_t in opSlice what about other types? c[ "a" .. "b"] or c[ "names", "A" .. "Z" ] c[ "numbers", 1 .. 9 ],=20 or more complicated slices... c[ "x", 2..5, "as" .. $, $ .. $-1, 4, 5]. ^a ^b where a is different from b. I dont know even what signature it will have, how to distinguish such uses: c[1, 2..3] c[1..2,3] mayby: opSlice(int, SliceTuple!(int, int)); opSlice(SliceTuple!(int, int), int); this then can be extended to things like: c [ 1 .. 2 .. 3] // SliceTuple!(int, int, int), ie. all paths // from 1 to 3 throught 3 or c [ 0 .. "9198231987397813" ] but what about $'s ? Not all of this is practical, but it will be good to have some elastic syntax for it. Creating temporary objects are very general, but why not have small syntactic sugar ([ .. ] is also syntactic sugar for opSlice call) or automatic creation of this objects and then rewriting this to: c[ "x" ][ 2..5 ][ "as" .. $ ][ $ .. $-1 ][ 4 ][ 5 ]. now all is simple. Every prefix will acumulate every pair and at the last call it will call some method. It can be good excerise to write some very general template mechanism for this. Generating temporary object is also good for things like: c[1..3][4..5] +=3D b[1..3][4..5]; it is quite trivial when using temporary object (overload opAddAssign). D have no opSlice*Assign (similary there is no opIndex*Assign). :/ --=20 Witold Baryluk, aleph0 MAIL: baryluk smp.if.uj.edu.pl JID: movax jabber.autocom.pl
Sep 11 2007
next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
 I dont know even what signature it will have, how to distinguish such
 uses:
 c[1, 2..3]
 c[1..2,3]
Just a suggestion, but the signature could be opSlice(int[] ...) where the passed array always had an even number of parameters, and non-slice parameters get passed as the tuple n, n+1. In that case, your two cases would be distinguished as [ 1, 2, 2, 3 ] and [ 1, 2, 3, 4 ] (since 1 is equivalent to 1..2, and 3 is equivalent to 3..4). Only works for ints, but hey! Better than nothing.
Sep 11 2007
parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Janice Caron wrote:
I dont know even what signature it will have, how to distinguish such
uses:
c[1, 2..3]
c[1..2,3]
Just a suggestion, but the signature could be opSlice(int[] ...) where the passed array always had an even number of parameters, and non-slice parameters get passed as the tuple n, n+1. In that case, your two cases would be distinguished as [ 1, 2, 2, 3 ] and [ 1, 2, 3, 4 ] (since 1 is equivalent to 1..2, and 3 is equivalent to 3..4). Only works for ints, but hey! Better than nothing.
If I may wax Pythonic, there is a better way: Slice objects. Take the following Python code: class C(object): def __getitem__(self, i): return i __getitem__ is roughly the equivalent of opIndex:
 c = C()
 c[10]
10 However, it handles slicing, as well:
 c[2:10]
slice(2, 10, None)
 c[1:5:2]
slice(1, 5, 2) Slices in Python have two or three items in them. The first two are the start and stop indices, which work just like in D. The third is an optional "step". If you set it to (e.g.) 2, you would get every other item. As the above code shows, it defaults to None. The slice object is very simple, and has three attributes of note, start, stop, and step, which are the values passed to the slice. If I may make up some syntax, we could invent a new "slice" type in D. I am admittedly not very good at making up syntax, but maybe something like this: alias void[int..int] slice; // a slice from an int to an int (A better keyword than "void" might be thought of.) You could then declare opIndex like this: class C { int[] arr; int opIndex(slice s) { return arr[s.start..s.stop]; // or just: return arr[s]; } } The examples mentioned previously become easy as pie. These: c[1, 2..3] c[1..2, 3] Would require the following opIndex methods: int opIndex(int, slice); int opIndex(slice, int); opSlice would, therefore, become entirely obsolete, unless I am missing some edge-case or other. This is possibly a little wild and crazy. I feel the basic idea is sound, however. What do other people think? -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Sep 11 2007
next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/11/07, Kirk McDonald <kirklin.mcdonald gmail.com> wrote:
 This is possibly a little wild and crazy. I feel the basic idea is
 sound, however. What do other people think?
Sounds good to me! Except that, if you had N dimensions, you'd have to overload the function N squared times, so it doesn't scale. A variadic function would fix that.
Sep 11 2007
prev sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Kirk McDonald wrote:
 Janice Caron wrote:
 I dont know even what signature it will have, how to distinguish such
 uses:
 c[1, 2..3]
 c[1..2,3]
Just a suggestion, but the signature could be opSlice(int[] ...) where the passed array always had an even number of parameters, and non-slice parameters get passed as the tuple n, n+1. In that case, your two cases would be distinguished as [ 1, 2, 2, 3 ] and [ 1, 2, 3, 4 ] (since 1 is equivalent to 1..2, and 3 is equivalent to 3..4). Only works for ints, but hey! Better than nothing.
If I may wax Pythonic, there is a better way: Slice objects. Take the following Python code: class C(object): def __getitem__(self, i): return i __getitem__ is roughly the equivalent of opIndex: >>> c = C() >>> c[10] 10 However, it handles slicing, as well: >>> c[2:10] slice(2, 10, None) >>> c[1:5:2] slice(1, 5, 2) Slices in Python have two or three items in them. The first two are the start and stop indices, which work just like in D. The third is an optional "step". If you set it to (e.g.) 2, you would get every other item. As the above code shows, it defaults to None. The slice object is very simple, and has three attributes of note, start, stop, and step, which are the values passed to the slice. If I may make up some syntax, we could invent a new "slice" type in D. I am admittedly not very good at making up syntax, but maybe something like this: alias void[int..int] slice; // a slice from an int to an int (A better keyword than "void" might be thought of.) You could then declare opIndex like this: class C { int[] arr; int opIndex(slice s) { return arr[s.start..s.stop]; // or just: return arr[s]; } } The examples mentioned previously become easy as pie. These: c[1, 2..3] c[1..2, 3] Would require the following opIndex methods: int opIndex(int, slice); int opIndex(slice, int); opSlice would, therefore, become entirely obsolete, unless I am missing some edge-case or other. This is possibly a little wild and crazy. I feel the basic idea is sound, however. What do other people think?
I fully agree and have proposed the same more than once. What I have been doing is something that would also handle $ properly. See attached file. The class template code for opIndex then need to handle arguments of types int, End, EndRel, EndMod, FullRange, Range, EndRange, EndRelRange, etc... This may all be a bit overkill. But it is a low overhead solution to the problem. And variadic templates make opIndex simple to implement. Template bloat is a problem though. -- Oskar
Sep 11 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Oskar Linde wrote:
 Kirk McDonald wrote:
 Janice Caron wrote:
 I dont know even what signature it will have, how to distinguish such
 uses:
 c[1, 2..3]
 c[1..2,3]
Just a suggestion, but the signature could be opSlice(int[] ...) where the passed array always had an even number of parameters, and non-slice parameters get passed as the tuple n, n+1. In that case, your two cases would be distinguished as [ 1, 2, 2, 3 ] and [ 1, 2, 3, 4 ] (since 1 is equivalent to 1..2, and 3 is equivalent to 3..4). Only works for ints, but hey! Better than nothing.
If I may wax Pythonic, there is a better way: Slice objects. Take the following Python code: class C(object): def __getitem__(self, i): return i __getitem__ is roughly the equivalent of opIndex: >>> c = C() >>> c[10] 10 However, it handles slicing, as well: >>> c[2:10] slice(2, 10, None) >>> c[1:5:2] slice(1, 5, 2) Slices in Python have two or three items in them. The first two are the start and stop indices, which work just like in D. The third is an optional "step". If you set it to (e.g.) 2, you would get every other item. As the above code shows, it defaults to None. The slice object is very simple, and has three attributes of note, start, stop, and step, which are the values passed to the slice. If I may make up some syntax, we could invent a new "slice" type in D. I am admittedly not very good at making up syntax, but maybe something like this: alias void[int..int] slice; // a slice from an int to an int (A better keyword than "void" might be thought of.) You could then declare opIndex like this: class C { int[] arr; int opIndex(slice s) { return arr[s.start..s.stop]; // or just: return arr[s]; } } The examples mentioned previously become easy as pie. These: c[1, 2..3] c[1..2, 3] Would require the following opIndex methods: int opIndex(int, slice); int opIndex(slice, int); opSlice would, therefore, become entirely obsolete, unless I am missing some edge-case or other. This is possibly a little wild and crazy. I feel the basic idea is sound, however. What do other people think?
I fully agree and have proposed the same more than once. What I have been doing is something that would also handle $ properly. See attached file. The class template code for opIndex then need to handle arguments of types int, End, EndRel, EndMod, FullRange, Range, EndRange, EndRelRange, etc... This may all be a bit overkill. But it is a low overhead solution to the problem. And variadic templates make opIndex simple to implement. Template bloat is a problem though.
I fully agree too. Some sort of slice object would make writing and using multi-dim array classes much easier. --bb
Sep 11 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Witold Baryluk wrote:
 
 It is also problem that you can only have size_t in opSlice
 what about other types?
 
 c[ "a" .. "b"]
I would love this. It would be quite useful for ordered associative sequences (red-black tree, etc). Sean
Sep 11 2007
parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Sean Kelly Wrote:

 Witold Baryluk wrote:
 
 It is also problem that you can only have size_t in opSlice
 what about other types?
 
 c[ "a" .. "b"]
I would love this. It would be quite useful for ordered associative sequences (red-black tree, etc). Sean
Got my vote!
Sep 11 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Robert Fraser" <fraserofthenight gmail.com> wrote in message 
news:fc70uu$o8r$1 digitalmars.com...
 Sean Kelly Wrote:
 I would love this.  It would be quite useful for ordered associative
 sequences (red-black tree, etc).
Got my vote!
...? class Blah { private int[char[]] mData; void opIndexAssign(int val, char[] key) { mData[key] = val; } int opIndex(char[] key) { return mData[key]; } int[] opSlice(char[] lo, char[] hi) { int[] ret; foreach(k, v; mData) if(k >= lo && k < hi) ret ~= v; return ret; } } void main() { scope b = new Blah(); b["a"] = 5; b["b"] = 10; b["hello"] = 13; b["x"] = 15; b["y"] = 20; b["z"] = 25; int[] x = b["b" .. "n"]; int[] y = b["x" .. "z"]; foreach(v; x) Stdout.format("{} ", v); Stdout.newline; foreach(v; y) Stdout.format("{} ", v); Stdout.newline; }
Sep 11 2007
next sibling parent Sean Kelly <sean f4.ca> writes:
Jarrett Billingsley wrote:
 "Robert Fraser" <fraserofthenight gmail.com> wrote in message 
 news:fc70uu$o8r$1 digitalmars.com...
 Sean Kelly Wrote:
 I would love this.  It would be quite useful for ordered associative
 sequences (red-black tree, etc).
Got my vote!
int[] opSlice(char[] lo, char[] hi)
I thought this worked, too. Ah well, next time I'll test before replying.

Yeah, that stinks. It's on the "to do" list somewhere. Sean
Sep 11 2007
prev sibling parent Witold Baryluk <baryluk smp.if.uj.edu.pl> writes:
Dnia Tue, 11 Sep 2007 18:08:12 -0400
"Jarrett Billingsley" <kb3ctd2 yahoo.com> napisa=B3/a:

 "Robert Fraser" <fraserofthenight gmail.com> wrote in message=20
 news:fc70uu$o8r$1 digitalmars.com...
 Sean Kelly Wrote:
 I would love this.  It would be quite useful for ordered
 associative sequences (red-black tree, etc).
Got my vote!
=20 ...? =20
[cut] Thanks. I have read D specification too literally :) Indeed this works: import std.stdio; class A { char[] opSlice(char[] x, char[] y) { return x ~ y; } char[] opSlice(char[] x, int y) { char[] z =3D ""; for (int i =3D 0; i < y; i++) { z ~=3D x; } return z; } } void main() { auto a =3D new A(); auto b =3D a["a" .. "z"]; writefln(b); // prints az auto c =3D a["a" .. 7]; writefln(c); // prints aaaaaaaa } --=20 Witold Baryluk, aleph0 MAIL: baryluk smp.if.uj.edu.pl JID: movax jabber.autocom.pl
Sep 11 2007
prev sibling parent reply S. <S s.com> writes:
Oskar Linde Wrote:

 Sean Kelly wrote:
 Russell Lewis wrote:
 It appears to me, looking at the spec, that there isn't a way to 
 support the use of $ in operator overloading.  Is there anything I've 
 overlooked?  What I'm looking for, of course, is to be able to do:

 class MyClass {....}

 MyClass c = new MyClass;
 auto x = c[1..$];
The '$' symbol should map to "size_t length()" when used with opSlice, but at the moment I don't think this is the case.
The problem how that generalizes to multidimensional slicing: c[1..$, 2..$] -- Oskar
Multidimensional slicing isn't mentioned in the language spec. When was this added? I would rather see $ be an alias for int.max for overloaded operators. This should be included in the spec, and then it is up to the overloaded operator to handle this to mean the last item. (either that or there could be another type added for indexes that includes this concept as a special bit sequence)
Sep 11 2007
parent reply Witold Baryluk <baryluk smp.if.uj.edu.pl> writes:
Dnia Tue, 11 Sep 2007 16:16:58 -0400
S. <S s.com> napisa=B3/a:


=20
 Multidimensional slicing isn't mentioned in the language spec.  When
 was this added?
Never. yet.
=20
 I would rather see $ be an alias for int.max for overloaded
 operators.  This should be included in the spec, and then it is up to
 the overloaded operator to handle this to mean the last item. (either
 that or there could be another type added for indexes that includes
 this concept as a special bit sequence)
Good, idea, but what about $-1, or $-100000000, it will not be easy to distinguish this from normal numbers. Things like c[1 .. $/2] are correct. Problem is also with other types, like strings. c["a" .. $-4]. Is $ some special value, some special symbol, or index of last item (or beyond last item)? Imho this should be a choice of user via some operator overloading. like char[] opDollar() but there will be problem with (hypothetical) multidimensional slices. (and also multidimensional arrays). c[1..$,2..$] each of dollar means something different c[$/2,$/3]. similary. mayby type1 opDollar_1() type2 opDollar_2() but not always second dollar is independend of first. for example let c be kind od triangular array, then $ here will be like second index in first subslice. c[1..4, 1..$] // $ =3D=3D 4 c[1..5, 1..$] // $ =3D=3D 5 Creating temporary objects are very very genneral for such constructions, but anyway we will need opDollar. --=20 Witold Baryluk, aleph0 MAIL: baryluk smp.if.uj.edu.pl JID: movax jabber.autocom.pl
Sep 12 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Witold Baryluk wrote:
 [snip]
 
 Imho this should be a choice of user via some operator overloading.
 like
 char[] opDollar()
 
 but there will be problem with (hypothetical) multidimensional slices.
 (and also multidimensional arrays).
 
 c[1..$,2..$]   each of dollar means something different
 
 c[$/2,$/3].   similary.
 
 
 
 mayby
 
 type1 opDollar_1()
 type2 opDollar_2()
 
 
 but not always second dollar is independend of first.
 
 
 for example
 
 let c be kind od triangular array, then $ here will be like
 second index in first subslice.
 
 c[1..4, 1..$]          // $ == 4
 c[1..5, 1..$]          // $ == 5
 
 
 
 Creating temporary objects are very very genneral for such
 constructions, but anyway we will need opDollar.
Two ideas: elemT[] opSlice(Repeat!(offset,2) xr, Repeat!(offset,2) yr); c[1..$,2..$] --> c.opSlice(offset(1), offset.dollar(0), offset(2), offset.dollar(0)); c[1..$,2..$-1] --> c.opSlice(offset(1), offset.dollar(0), offset(2), offset.dollar(-1)); You could create such a type by sacrificing the upper-most bit of a size_t type; if that bit is set, treat it as a one's complement signed offset from whatever "dollar" happens to be. Other idea: Tuple!(size_t, size_t) opDollar(); Or, if we aren't allowed to have tuple return types: void opDollar(out size_t, out size_t); That gives us multidimensional dollars. Come to think of it, the second is probably the better syntax since it will allow for overloading based on how many dimensions the user is indexing on. This could be useful for, say, matrices which could be accessed as columns of vectors, or individual scalars. Just something to chew on :) -- Daniel
Sep 12 2007
parent Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
Daniel Keep wrote:

 Two ideas:
 
 elemT[] opSlice(Repeat!(offset,2) xr, Repeat!(offset,2) yr);
 
 c[1..$,2..$] --> c.opSlice(offset(1), offset.dollar(0),
                            offset(2), offset.dollar(0));
 c[1..$,2..$-1] --> c.opSlice(offset(1), offset.dollar(0),
                              offset(2), offset.dollar(-1));
 
 You could create such a type by sacrificing the upper-most bit of a
 size_t type; if that bit is set, treat it as a one's complement signed
 offset from whatever "dollar" happens to be.
 
 Other idea:
 
 Tuple!(size_t, size_t) opDollar();
 
 Or, if we aren't allowed to have tuple return types:
 
 void opDollar(out size_t, out size_t);
 
 That gives us multidimensional dollars.  Come to think of it, the second
 is probably the better syntax since it will allow for overloading based
 on how many dimensions the user is indexing on.  This could be useful
 for, say, matrices which could be accessed as columns of vectors, or
 individual scalars.
 
 Just something to chew on :)
Or what about retType opDollar(int dollar_idx = 0)(idxType); It would also need some changes in the way compiler handles templated methods and template default parameters currently. But I live in faith that those will change :) A bit OT, but I was browsing some older posts and came across several instances of amazingly similar concepts that have been discussed lately (for example ndim slices, macros and multiple return values). That just makes me wonder whether time really is linear or are we stuck in an eternal loop (modulo 42) ? :)
Sep 12 2007
prev sibling parent Matti Niemenmaa <see_signature for.real.address> writes:
Russell Lewis wrote:
 It appears to me, looking at the spec, that there isn't a way to support
 the use of $ in operator overloading.  Is there anything I've
 overlooked?
Nope, no way. Requests for opDollar or the equivalent have been made, but no response so far. -- E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
Sep 11 2007