digitalmars.D - Suggestion: dynamic array operations

• Nikita Kalaganov (60/60) Sep 06 2006 1. Pop from array on front/back
• Gregor Richards (7/89) Sep 06 2006 Arrays are arrays, not lists. You can abuse them into stacks, queues
• Steve Horne (19/21) Sep 06 2006 Sorry for pedanting, but it's an inefficient use of CPU cycles, not
• Gregor Richards (8/34) Sep 06 2006 To pop an element off the front of an array requires either losing that
• Gregor Richards (4/40) Sep 06 2006 Erm, totally misread everything. Wowza.
• Sean Kelly (15/56) Sep 06 2006 Not necessarily. Appending to an array that doubles its size on
• Steve Horne (38/42) Sep 07 2006 No problem.
• nobody (4/21) Sep 07 2006 So what sort of silly stuff would you write about possible benefits deri...
• Steve Horne (24/26) Sep 07 2006 There's already a built-in type for the links. It's called 'pointers'
• nobody (19/43) Sep 08 2006 A pointer is a pointer ;-)
• Steve Horne (12/21) Sep 08 2006 Disagree. Accessing a list element requires a list position. That
• Sean Kelly (10/17) Sep 07 2006 Definitely. I'm mostly supporting arrays at the moment because they're
• Chris Nicholson-Sauls (24/44) Sep 06 2006 T[] pull (T) (T[] src, size_t len) {
• Steve Horne (80/84) Sep 06 2006 ...
"Nikita Kalaganov" <sapfirnik rambler.ru> writes:
```1. Pop from array on front/back

Syntax:
ARRAY1 =3D ARRAY2 <<< INTEGER
ARRAY1 <<<=3D INTEGER

ARRAY1 =3D ARRAY2 >>> INTEGER
ARRAY1 >>>=3D INTEGER

Example:
int[] a;
// let a =3D [1,2,3,4,5]
int[] b;

b =3D a <<< 4; // b =3D [1,2,3,4] a =3D [5]
b =3D a >>> 4; // b =3D [2,3,4,5] a =3D [1]

a <<<=3D 3; // a =3D [1,2,3]
a >>>=3D 1; // a =3D [3]

2. Array rotation

Syntax:
ARRAY1 =3D ARRAY2 ral INTEGER
ARRAY1 ral=3D INTEGER

ARRAY1 =3D ARRAY2 rar INTEGER
ARRAY1 rar=3D INTEGER

Example:
int[] a;
// let a =3D [1,2,3,4,5]

b =3D a ral 2; // b =3D [3,4,5,1,2]
b =3D a rar 2; // b =3D [4,5,1,2,3]

a ral=3D 2; // a =3D [3,4,5,1,2]
a rar=3D 4; // a =3D [4,5,1,2,3]

3. Shift array left/right

Syntax:
ARRAY1 =3D ARRAY2 << {ARRAY3 | INTEGER}
ARRAY1 <<=3D {ARRAY2 | INTEGER}

ARRAY1 =3D ARRAY2 >> {ARRAY3 | INTEGER}
ARRAY1 >>=3D {ARRAY2 | INTEGER}

Example ('di' means default initializer):
int[] a;
// let a =3D [1,2,3,4,5]
int[] b;
// let b =3D [6,7];
int[] c;

c =3D a << 3;	// c =3D [4,5,di,di,di]
c =3D a << b;	// c =3D [3,4,5,6,7]

c =3D a >> 3;	// c =3D [di,di,di,1,2]
c =3D a >> b;	// c =3D [6,7,1,2,3]

a <<=3D 2;	// a =3D [3,4,5,di,di]
a <<=3D b;	// a =3D [5,di,di,6,7]
a >>=3D 2;	// a =3D [di,di,5,di,0]
a >>=3D b;	// a =3D [6,7,di,di,5]

------------

Together with concatenation, these operations give us in-built standard =
=

containers like queue and deque (goodbye templates!). Also, they ease  =

implementation of various algorithms with matrix operations.

P.S. Hmm, maybe it's a good idea to add rotation operations for _unsigne=
d_  =

integers ?

Syntax & example:
uint a;

a =3D 0x000ff000;
a rol=3D 4; // a =3D 0x00ff0000
a ror=3D 20; // a =3D 0xf000000f
```
Sep 06 2006
Gregor Richards <Richards codu.org> writes:
```Nikita Kalaganov wrote:

1. Pop from array on front/back

Syntax:
ARRAY1 = ARRAY2 <<< INTEGER
ARRAY1 <<<= INTEGER

ARRAY1 = ARRAY2 >>> INTEGER
ARRAY1 >>>= INTEGER

Example:
int[] a;
// let a = [1,2,3,4,5]
int[] b;

b = a <<< 4; // b = [1,2,3,4] a = [5]
b = a >>> 4; // b = [2,3,4,5] a = [1]

a <<<= 3; // a = [1,2,3]
a >>>= 1; // a = [3]

2. Array rotation

Syntax:
ARRAY1 = ARRAY2 ral INTEGER
ARRAY1 ral= INTEGER

ARRAY1 = ARRAY2 rar INTEGER
ARRAY1 rar= INTEGER

Example:
int[] a;
// let a = [1,2,3,4,5]

b = a ral 2; // b = [3,4,5,1,2]
b = a rar 2; // b = [4,5,1,2,3]

a ral= 2; // a = [3,4,5,1,2]
a rar= 4; // a = [4,5,1,2,3]

3. Shift array left/right

Syntax:
ARRAY1 = ARRAY2 << {ARRAY3 | INTEGER}
ARRAY1 <<= {ARRAY2 | INTEGER}

ARRAY1 = ARRAY2 >> {ARRAY3 | INTEGER}
ARRAY1 >>= {ARRAY2 | INTEGER}

Example ('di' means default initializer):
int[] a;
// let a = [1,2,3,4,5]
int[] b;
// let b = [6,7];
int[] c;

c = a << 3;    // c = [4,5,di,di,di]
c = a << b;    // c = [3,4,5,6,7]

c = a >> 3;    // c = [di,di,di,1,2]
c = a >> b;    // c = [6,7,1,2,3]

a <<= 2;    // a = [3,4,5,di,di]
a <<= b;    // a = [5,di,di,6,7]
a >>= 2;    // a = [di,di,5,di,0]
a >>= b;    // a = [6,7,di,di,5]

------------

Together with concatenation, these operations give us in-built standard
containers like queue and deque (goodbye templates!). Also, they ease
implementation of various algorithms with matrix operations.

P.S. Hmm, maybe it's a good idea to add rotation operations for
_unsigned_ integers ?

Syntax & example:
uint a;

a = 0x000ff000;
a rol= 4; // a = 0x00ff0000
a ror= 20; // a = 0xf000000f

Arrays are arrays, not lists.  You can abuse them into stacks, queues
and the ilk, but it's an inefficient use of memory.  There's a reason
that stacks, queues and such are not usually implemented with arrays, in
the same way that constant strings are not generally implemented as
linear linked lists of single characters.

- Gregor Richards
```
Sep 06 2006
Steve Horne <stephenwantshornenospam100 aol.com> writes:
```On Wed, 06 Sep 2006 10:51:32 -0700, Gregor Richards
<Richards codu.org> wrote:

Arrays are arrays, not lists.  You can abuse them into stacks, queues
and the ilk, but it's an inefficient use of memory.

Sorry for pedanting, but it's an inefficient use of CPU cycles, not
memory. Arrays are more memory efficient than linked lists - no
per-item pointers, and only a one-off malloc overhead.

Even for time, it depends on how big the stack/deque will get. For
small sizes arrays are a good choice. Realloc problems are minimised,
time to shift the items up/down is trivial, and there are cache
benefits to keeping the items in adjacent memory locations.

For large deques, what you really want is a list of small arrays. A
trade-off between the costs and benefits of both lists and arrays.
IIRC this is pretty much what std::deque does in C++. But even then,
pushing and popping on the small arrays is needed to implement the
full deque logic.

And of course, that large deque class could overload those operators
to allow the same syntax.

I just think methods are a better choice than operators for this.

--
Remove 'wants' and 'nospam' from e-mail.
```
Sep 06 2006
Gregor Richards <Richards codu.org> writes:
```Steve Horne wrote:
On Wed, 06 Sep 2006 10:51:32 -0700, Gregor Richards
<Richards codu.org> wrote:

Arrays are arrays, not lists.  You can abuse them into stacks, queues
and the ilk, but it's an inefficient use of memory.

Sorry for pedanting, but it's an inefficient use of CPU cycles, not
memory. Arrays are more memory efficient than linked lists - no
per-item pointers, and only a one-off malloc overhead.

Even for time, it depends on how big the stack/deque will get. For
small sizes arrays are a good choice. Realloc problems are minimised,
time to shift the items up/down is trivial, and there are cache
benefits to keeping the items in adjacent memory locations.

For large deques, what you really want is a list of small arrays. A
trade-off between the costs and benefits of both lists and arrays.
IIRC this is pretty much what std::deque does in C++. But even then,
pushing and popping on the small arrays is needed to implement the
full deque logic.

And of course, that large deque class could overload those operators
to allow the same syntax.

I just think methods are a better choice than operators for this.

To pop an element off the front of an array requires either losing that
chunk of memory or moving the entire rest of the array back such that
that memory is still used.  Pushing an element onto either side of an
array involves expanding the memory used by that array, which often
involves duplication.  Using an array as a stack or queue is a waste of
MEMORY, not CPU cycles.

- Gregor Richards
```
Sep 06 2006
Gregor Richards <Richards codu.org> writes:
```Gregor Richards wrote:
Steve Horne wrote:
On Wed, 06 Sep 2006 10:51:32 -0700, Gregor Richards
<Richards codu.org> wrote:

Arrays are arrays, not lists.  You can abuse them into stacks, queues
and the ilk, but it's an inefficient use of memory.

Sorry for pedanting, but it's an inefficient use of CPU cycles, not
memory. Arrays are more memory efficient than linked lists - no
per-item pointers, and only a one-off malloc overhead.

Even for time, it depends on how big the stack/deque will get. For
small sizes arrays are a good choice. Realloc problems are minimised,
time to shift the items up/down is trivial, and there are cache
benefits to keeping the items in adjacent memory locations.

For large deques, what you really want is a list of small arrays. A
trade-off between the costs and benefits of both lists and arrays.
IIRC this is pretty much what std::deque does in C++. But even then,
pushing and popping on the small arrays is needed to implement the
full deque logic.

And of course, that large deque class could overload those operators
to allow the same syntax.

I just think methods are a better choice than operators for this.

To pop an element off the front of an array requires either losing that
chunk of memory or moving the entire rest of the array back such that
that memory is still used.  Pushing an element onto either side of an
array involves expanding the memory used by that array, which often
involves duplication.  Using an array as a stack or queue is a waste of
MEMORY, not CPU cycles.

- Gregor Richards

Yeah, that'd be CPU cycles :P

- Gregor Richards
```
Sep 06 2006
Sean Kelly <sean f4.ca> writes:
```Gregor Richards wrote:
Gregor Richards wrote:
Steve Horne wrote:
On Wed, 06 Sep 2006 10:51:32 -0700, Gregor Richards
<Richards codu.org> wrote:

Arrays are arrays, not lists.  You can abuse them into stacks,
queues and the ilk, but it's an inefficient use of memory.

Sorry for pedanting, but it's an inefficient use of CPU cycles, not
memory. Arrays are more memory efficient than linked lists - no
per-item pointers, and only a one-off malloc overhead.

Even for time, it depends on how big the stack/deque will get. For
small sizes arrays are a good choice. Realloc problems are minimised,
time to shift the items up/down is trivial, and there are cache
benefits to keeping the items in adjacent memory locations.

For large deques, what you really want is a list of small arrays. A
trade-off between the costs and benefits of both lists and arrays.
IIRC this is pretty much what std::deque does in C++. But even then,
pushing and popping on the small arrays is needed to implement the
full deque logic.

And of course, that large deque class could overload those operators
to allow the same syntax.

I just think methods are a better choice than operators for this.

To pop an element off the front of an array requires either losing
that chunk of memory or moving the entire rest of the array back such
that that memory is still used.  Pushing an element onto either side
of an array involves expanding the memory used by that array, which
often involves duplication.  Using an array as a stack or queue is a
waste of MEMORY, not CPU cycles.

- Gregor Richards

Yeah, that'd be CPU cycles :P

Not necessarily.  Appending to an array that doubles its size on
reallocations is an amortized O(1) operation.  And since arrays display
far better locality than linked-lists, the potentially far superior
cache performance can result in a noticeable performance increase over
linked-list implementations of some data structures.  Stacks are
relatively trivial to implement in arrays (push/pop from the back), and
even queues can be made fairly efficient (if there is a soft upper bound
on their size) by using a circular array.  Heaps and binary trees do
fairly well in arrays also, since insertions and deletions do not
require moving data.  That isn't to say that arrays are always the best
choice for implementing such data structures, but I think it's a much
grayer area than you're suggesting.  Particularly when real-world issues
such as cache misses come into play.

Sean
```
Sep 06 2006
Steve Horne <stephenwantshornenospam100 aol.com> writes:
```On Wed, 06 Sep 2006 15:43:42 -0700, Sean Kelly <sean f4.ca> wrote:

Gregor Richards wrote:

No problem.

To be honest, I regretted posting. I have a habit of being too abrupt
and causing offense. Plus I have a lot of time on my hands ATM, and
getting bored and writing reams on silly stuff can easily end up
looking like criticism. Sorry if thats what happened.

Not necessarily.  Appending to an array that doubles its size on
reallocations is an amortized O(1) operation.

That reminds me of an article in Dr. Dobbs, back when I subscribed.
Got a few good ideas from that. It might be time to get their latest
CD again, if I can afford it ;-)

Array size doubling can sound like a terrible waste of memory, or at
least it did to me when I first read about it. But then again,

I have a thing about multiway trees (B trees, B+ trees, etc). In
memory, not just on disk. Compared with binary trees, they have better
locality and cause less heap fragmentation for the same reasons that
arrays do, though not to the same extreme. But if you like multiway
trees, it is absurd to complain about the memory waste when doing
array size doubling. Both guarantee that the memory allocated is at
least 50% used, barring very very small datasets, but multiway trees
have per-node overheads. And (for B+-style trees) branch nodes that
don't hold data items at all.

Data structures are kind of a horses for courses thing. These days,
you can get away with a lot while using whatevers to hand. Bung a few
thousand items in any container and you won't have much to worry
about. Custom allocators can fix a lot of problems even when they do
show up. Even so, those choices always have costs as well as benefits,
as the dataset gets bigger, and those costs can sometimes be a big
problem.

I was surprised to see hash tables built into D. I had that one in my
'scripting language' stereotype. But it's probably a good thing.

But I still think there should be alternative containers in the
library (assuming they're not there anyway and I've just missed them).
I know the term 'perfect hash', but the term is precisely that - not
'perfect container'! There are things that hashtables can't do (or at
least, can't do very efficiently for large datasets) like in-order
iterating or picking out an ordered subrange.

--
Remove 'wants' and 'nospam' from e-mail.
```
Sep 07 2006
nobody <nobody mailinator.com> writes:
```Steve Horne wrote:
On Wed, 06 Sep 2006 15:43:42 -0700, Sean Kelly <sean f4.ca> wrote:

Gregor Richards wrote:

No problem.

To be honest, I regretted posting. I have a habit of being too abrupt
and causing offense. Plus I have a lot of time on my hands ATM, and
getting bored and writing reams on silly stuff can easily end up
looking like criticism. Sorry if thats what happened.

...

But I still think there should be alternative containers in the
library (assuming they're not there anyway and I've just missed them).
I know the term 'perfect hash', but the term is precisely that - not
'perfect container'! There are things that hashtables can't do (or at
least, can't do very efficiently for large datasets) like in-order
iterating or picking out an ordered subrange.

So what sort of silly stuff would you write about possible benefits derived
from
having a list data structure built into the language?
```
Sep 07 2006
Steve Horne <stephenwantshornenospam100 aol.com> writes:
```On Thu, 07 Sep 2006 11:19:12 -0400, nobody <nobody mailinator.com>
wrote:

So what sort of silly stuff would you write about possible benefits derived
from
having a list data structure built into the language?

;-)

There's just too many ways to do a linked list. Singly linked. Doubly
linked. Circular or with a clear start and end. Do you want separate
whole-list and iterator classes, or do you want to package the two in
a single class (as with a stack).

Bidirection lists implemented with a single link per item are fairly
cool, even though the iterators are a bit of a pain - each link is the
XOR of the previous and next item addresses, and the iterator needs
benefit, but still good for listing very small items.

Picked it up from a website by an ex-Microsoft employee, IIRC. He used

A small set of built-in complex types is good, but libraries and
templates exist for a reason. If a standard library list type gets
really heavy use, it can always be absorbed into the language later.

Mind you, I guess a complex list-type specification might have
benefits along the lines of...

But templates can do that. And anyway, lists are naff! I like trees!

--
Remove 'wants' and 'nospam' from e-mail.
```
Sep 07 2006
nobody <nobody mailinator.com> writes:
```Steve Horne wrote:
On Thu, 07 Sep 2006 11:19:12 -0400, nobody <nobody mailinator.com>
wrote:

So what sort of silly stuff would you write about possible benefits derived
from
having a list data structure built into the language?

Thanks for taking the time to respond.

;-)

A pointer is a pointer ;-)

Accessing a pointer to a value requires just the pointer. Accessing an array
element requires a pointer and an offset. Accessing a list element requires a
pointer and a depth.

There's just too many ways to do a linked list. Singly linked. Doubly
linked. Circular or with a clear start and end. Do you want separate
whole-list and iterator classes, or do you want to package the two in
a single class (as with a stack).

Bidirection lists implemented with a single link per item are fairly
cool, even though the iterators are a bit of a pain - each link is the
XOR of the previous and next item addresses, and the iterator needs
benefit, but still good for listing very small items.

Picked it up from a website by an ex-Microsoft employee, IIRC. He used

These are all great examples of how one might use a basic list structure. I had
the Lisp cons cell in mind and was apparently expecting you to discover that
via
ESP -- another great feature D will probably never have. The cons cell as I was
imagining it has room for two pointers. The first is usually known as the car
of
the list and the second is the cdr:

http://en.wikipedia.org/wiki/Car_and_cdr

Introduced in the Lisp programming language, car (IPA ['kar], just like the
English word "car") and cdr (IPA ['k? d?r] or ['ku d?r]) are primitive
operations upon linked lists composed of cons cells. A cons cell is composed
of two pointers; the car operation extracts the first pointer, and the cdr
operation extracts the second.

A small set of built-in complex types is good, but libraries and
templates exist for a reason. If a standard library list type gets
really heavy use, it can always be absorbed into the language later.

The great thing about a cons cell is the cons cell alone is not any particular
flavor of list or tree. You can build all sorts of interesting data types.
```
Sep 08 2006
Steve Horne <stephenwantshornenospam100 aol.com> writes:
```On Fri, 08 Sep 2006 09:48:36 -0400, nobody <nobody mailinator.com>
wrote:

Accessing a pointer to a value requires just the pointer. Accessing an array
element requires a pointer and an offset. Accessing a list element requires a
pointer and a depth.

Disagree. Accessing a list element requires a list position. That
could be as simple as an iterator - a pointer, IOW. If I was going to
subscript into a container, I probably wouldn't use a linked list.

These are all great examples of how one might use a basic list structure. I had
the Lisp cons cell in mind and was apparently expecting you to discover that
via
ESP -- another great feature D will probably never have. The cons cell as I was
imagining it has room for two pointers. The first is usually known as the car
of
the list and the second is the cdr:

http://en.wikipedia.org/wiki/Car_and_cdr

I've Lisped in a past life, though not much. I dunno, though. After
all, it's a low level tool - not a high level container. When a tool
can do anything, its not always clear what it's supposed to be doing,
so you won't be told when you're doing it wrong.

But then, it is just a struct with two pointers.

--
Remove 'wants' and 'nospam' from e-mail.
```
Sep 08 2006
Sean Kelly <sean f4.ca> writes:
```Steve Horne wrote:

But I still think there should be alternative containers in the
library (assuming they're not there anyway and I've just missed them).
I know the term 'perfect hash', but the term is precisely that - not
'perfect container'! There are things that hashtables can't do (or at
least, can't do very efficiently for large datasets) like in-order
iterating or picking out an ordered subrange.

Definitely.  I'm mostly supporting arrays at the moment because they're
built-in, and I haven't seen a container library for D yet that I'm
convinced is the perfect solution.  But eventually, I'd like to revert
to an iterator sort of approach instead.  Oskar mentioned recently that
we can sort of do that now by supporting anything with opIndex and a
length property, but I'm not sure I see a tremendous value there over
arrays--largely because I can't think of many objects I'd implement with
these methods that I'd want to run algorithms on.

Sean
```
Sep 07 2006
Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
```Nikita Kalaganov wrote:

1. Pop from array on front/back

Syntax:
ARRAY1 = ARRAY2 <<< INTEGER
ARRAY1 <<<= INTEGER

ARRAY1 = ARRAY2 >>> INTEGER
ARRAY1 >>>= INTEGER

Example:
int[] a;
// let a = [1,2,3,4,5]
int[] b;

b = a <<< 4; // b = [1,2,3,4] a = [5]
b = a >>> 4; // b = [2,3,4,5] a = [1]

a <<<= 3; // a = [1,2,3]
a >>>= 1; // a = [3]

T[] pull (T) (T[] src, size_t len) {
T[] result ;

len    = len >= src.length ? src.length : len + 1;
result = src[0 .. len];
src    = src[len .. src.length];

return result;
}

T[] rpull (T) (T[] src, size_t len) {
T[] result ;

len    = len >= src.length ? src.length : len + 1;
result = src[src.length - len .. src.length];
src    = src[0 .. len];

return result;
}

int[] a ;
int[] b ;
a = array!(int)(1, 2, 3, 4, 5);

b = a.pull(4); // a == [5], b == [1, 2, 3, 4]

b = a.rpull(4); // a == [1], b == [2, 3, 4, 5]

This is precisely why I say we need an array utils module in Phobos.  I have
one in
progress in my Cashew project, but it is incomplete and could use updating now
that D's
import system has been improved.

-- Chris Nicholson-Sauls
```
Sep 06 2006
Steve Horne <stephenwantshornenospam100 aol.com> writes:
```On Wed, 06 Sep 2006 20:04:29 +0400, "Nikita Kalaganov"
<sapfirnik rambler.ru> wrote:

1. Pop from array on front/back

Syntax:

...
ARRAY1 = ARRAY2 >>> INTEGER
ARRAY1 >>>= INTEGER

I think additional built-in methods for array types would be the best

'Push' and 'pop' are as clear as it gets. I don't find the C++ STL
push_back and push_front very intuitive - which end is the front? -
but, since I mentally lay out my subscripts the same as my cartesian
co-ordinates, push_left and pop_left work for me. And rotate methods
for arrays strike me as a very rare requirement, but what the hell ;-)

As for your operators, for one thing, the ones above already exist. D
has both signed and unsigned bitshift right operators. Don't ask me
why - I don't see the problem with shifting consistently with the
datatype, as C and C++ do.

Mind you, operators can be overloaded. I may be in a minority around
here, given that D has chosen a different route for I/O, but
personally I like the way that C++ overloads the << operator as
'stream insertion'. And I have no problem with the idea of extending
that so a 'stream' is any sequence of items. On that basis, why not...

array1 << value-to-push;
array2 >> var-to-pop-into;

Well, there is that little problem I have. While I like C++ stream
insertion, stream extraction is something else. It reads wrong.

Also, this goes against the flow of what D already defines. Pushing a
new item can already be done with...

array1 ~= value-to-push;

D already defines all those wierd relative operators that handle NAN
for floats, so maybe there's room for taking '!' as 'opposite-of'
rather than just 'not'...

array1 !~= var-to-pop;

But a major issue I have with that is that the item that is assigned
is the right hand argument, not the usual left hand argument that is
traditionally assigned by all C/C++/D/etc assignment operators.

Which goes back to the problem I have with stream extraction. If it
was written as...

var-to-pop-into << array1;

or...

var-to-pop-into <<= array1;

I would have less of a problem. And, as with multi-assignment
expressions, I would have less of a problem with...

var1 << var2 << var3 << array1;

It reads better, to my eyes. It suggests items flowing out of the
array until the required number have flowed out, into the specified
slots. Beads sliding down string or something like that. The C++
stream extraction form...

array1 >> var1 >> var2 >> var3;

Seems to suggest the same flow-like system, but the implied order is
the opposite of what actually happens.

But using the same '<<' or '<<=' or whatever for both means that it is
down to overload resolution to decide whether to insert or extract. If
the left argument is a 'stream', insert. If the right argument is a
'stream', extract. But if a 'stream' is just taken as referring to any
sequential data, what about arrays of arrays? What does the following
mean?

varname << stringvar;

Is it inserting a string into an array of strings? Is it extracting a
character from a string?  For that matter, is it concatenating two
strings?

types are different. Only concatenation has both arguments the same
type. But it isn't just the compiler that has to resolve the

Basically, you have an operator with three clearly different meanings
allow operators with a consistent meaning to work with new types - for
example, supporting '+' on new numeric types (perhaps decimal floats,
rationals) that the language doesn't have built in. It just seems a
bit too cryptic. Though you could claim that the meaning of '<<' was
something to do with flowing or shifting in general.

Also, I don't think deque operations and rotations are done often
enough to justify an operator. For example, those matrix algorithms of
yours should be written once and packaged in a library. Methods aren't
that much of a hardship.

But then, I already think the D relative operators are a bit nuts.
You'd have to be using floating point and caring about NAN a lot to
get used to them. Short of that, any gains from clutter clearing are
going to be lost referring back to the manuals. But there are
obviously people around who see it differently.

--
Remove 'wants' and 'nospam' from e-mail.
```
Sep 06 2006