Jump to page: 1 24  
Page
Thread overview
AA revisited
Jul 12, 2005
David Medlock
Jul 12, 2005
Shammah Chancellor
Jul 12, 2005
David Medlock
Jul 13, 2005
Chris Sauls
Jul 12, 2005
Ben Hinkle
Jul 12, 2005
David Medlock
Jul 12, 2005
Ben Hinkle
Jul 12, 2005
David Medlock
Jul 12, 2005
Ben Hinkle
Jul 12, 2005
David Medlock
Jul 12, 2005
Andrew Fedoniouk
Jul 12, 2005
Ben Hinkle
Jul 12, 2005
Andrew Fedoniouk
Jul 12, 2005
Concerned
Jul 12, 2005
Regan Heath
Jul 13, 2005
Regan Heath
Jul 13, 2005
Nick
Jul 13, 2005
David Medlock
Jul 13, 2005
sq
Jul 14, 2005
David Medlock
Jul 12, 2005
Shammah Chancellor
Jul 12, 2005
Shammah Chancellor
Jul 12, 2005
Ben Hinkle
Jul 12, 2005
Shammah Chancellor
Jul 12, 2005
Ben Hinkle
Jul 12, 2005
David Medlock
Jul 12, 2005
Andrew Fedoniouk
Jul 12, 2005
Andrew Fedoniouk
Jul 13, 2005
Charles Hixson
Jul 13, 2005
Andrew Fedoniouk
Jul 13, 2005
me
July 12, 2005
I have to say the AA changes are confusing to say the least.

With classes using  'lookup[key]'  would create an (key,value) *entry* but not an *object*, so you still must check for null and create if necessary.  The amount of code here is basically the same whether you use *in* or just aa[key].  Since you are using an object reference, you can change the object in the AA directly and there is no real need for pointers.

We now have 3 possibilities for AA which return classes:

1. Key is not in the AA, must check for this or Exception thrown.
2. Key is in the AA, but the reference stored there is null.
3. Key is in the AA, reference points to an object.

void CallMethod( MyClass c, int key, MyClass[int]  lookup )
{
  MyClass* ptr = (key in lookup);
  if ( ptr is null )  lookup[key] = new MyClass();

  MyClass ref = lookup[key];
  if ( ref is null )
  {
    ref = new MyClass();
    lookup[key]= ref;
  }
  ref.Method();
}

This is starting to make C++ look clean.

Structs on the other hand are supposed to be implicitly created.
They are *values* not objects.

So now we have 2 coding idioms/paradigms for structs:
1. Implicitly create when declared in code.

struct S {
	int n = 99;
	void callme(){ n++ ; }
}

S var1, var2, var3;  // created as declared
var1.callme();       // no problem, I want the defaults, I defined them

2. Struct value in an AA.

S[int] lookup;
S[100].callme() ; //Now this statement requires 7-10 lines of code.

All this because someone was concerned with a double lookup the *first time* an object isnt found in the AA?  Semantical madness over something that isnt even a real deficiency.

Premature optimization is a root cause of bad programming, because you end up making hard to follow code in attempts to optimize before you even know what is slow.

This is premature optimization at the *language level*.
-DavidM
July 12, 2005
In article <db0p66$vfj$1@digitaldaemon.com>, David Medlock says...
>
>I have to say the AA changes are confusing to say the least.
>
>With classes using  'lookup[key]'  would create an (key,value) *entry* but not an *object*, so you still must check for null and create if necessary.  The amount of code here is basically the same whether you use *in* or just aa[key].  Since you are using an object reference, you can change the object in the AA directly and there is no real need for pointers.
>
>We now have 3 possibilities for AA which return classes:
>
>1. Key is not in the AA, must check for this or Exception thrown.
>2. Key is in the AA, but the reference stored there is null.
>3. Key is in the AA, reference points to an object.
>
>void CallMethod( MyClass c, int key, MyClass[int]  lookup )
>{
>   MyClass* ptr = (key in lookup);
>   if ( ptr is null )  lookup[key] = new MyClass();
>
>   MyClass ref = lookup[key];
>   if ( ref is null )
>   {
>     ref = new MyClass();
>     lookup[key]= ref;
>   }
>   ref.Method();
>}
>
>This is starting to make C++ look clean.
>
>Structs on the other hand are supposed to be implicitly created. They are *values* not objects.
>
>So now we have 2 coding idioms/paradigms for structs:
>1. Implicitly create when declared in code.
>
>struct S {
>	int n = 99;
>	void callme(){ n++ ; }
>}
>
>S var1, var2, var3;  // created as declared
>var1.callme();       // no problem, I want the defaults, I defined them
>
>2. Struct value in an AA.
>
>S[int] lookup;
>S[100].callme() ; //Now this statement requires 7-10 lines of code.
>
>All this because someone was concerned with a double lookup the *first time* an object isnt found in the AA?  Semantical madness over something that isnt even a real deficiency.
>
>Premature optimization is a root cause of bad programming, because you end up making hard to follow code in attempts to optimize before you even know what is slow.
>
>This is premature optimization at the *language level*. -DavidM

Some of the code you mentioned has already been deprecated.

aa[key] now throws an exception if it does not exist.  Thus aa[newkey] = new Class is now invalid.  You must instead call aa.Add(key)

(See changes for DMD 126: Now throws an ArrayBoundsError if accessing an associative array with a key that is not already in the array. Previously, the key would be added to the array. )

Thus your code would change to something like this

:try {
:    foo = aa[key];
:} catch ( Exception e ) {
:    foo = aa.Add(key, new MyClass());
:} finally {
:    foo.Method();
:}

Error handling sucks.  Personally I think AA syntax is starting to transcend integral types.  Since when does an integer throw an exception?  I'm all for making AA's a part of phobos and separating them from the language syntax.


Also, these silly functions properties that are accessed like members are very confusing.   Like this:


void HelloWorld(int foo) {
writeln("Hi world, I have %d foos", foo);
}

int foo;

foo.HelloWorld();

^-- AAARGGGH!!!  Confusion ensues!  I'd like to see this syntax go away too. It's not that much effort for people who want to extend the integer type to type this:

HelloWorld(foo);  (which is less typing By the way, notice the lack of the extra
period)

Which is how it belongs since it's not part of the integer type, and you can't find it in the class file for integer.



July 12, 2005
Shammah Chancellor wrote:
> In article <db0p66$vfj$1@digitaldaemon.com>, David Medlock says...
> 
>>I have to say the AA changes are confusing to say the least.
>>
>>With classes using  'lookup[key]'  would create an (key,value) *entry* but not an *object*, so you still must check for null and create if necessary.  The amount of code here is basically the same whether you use *in* or just aa[key].  Since you are using an object reference, you can change the object in the AA directly and there is no real need for pointers.
>>
>>We now have 3 possibilities for AA which return classes:
>>
>>1. Key is not in the AA, must check for this or Exception thrown.
>>2. Key is in the AA, but the reference stored there is null.
>>3. Key is in the AA, reference points to an object.
>>
>>void CallMethod( MyClass c, int key, MyClass[int]  lookup )
>>{
>>  MyClass* ptr = (key in lookup);
>>  if ( ptr is null )  lookup[key] = new MyClass();
>>
>>  MyClass ref = lookup[key];
>>  if ( ref is null )
>>  {
>>    ref = new MyClass();
>>    lookup[key]= ref;
>>  }
>>  ref.Method();
>>}
>>
>>This is starting to make C++ look clean.
>>
>>Structs on the other hand are supposed to be implicitly created.
>>They are *values* not objects.
>>
>>So now we have 2 coding idioms/paradigms for structs:
>>1. Implicitly create when declared in code.
>>
>>struct S {
>>	int n = 99;
>>	void callme(){ n++ ; }
>>}
>>
>>S var1, var2, var3;  // created as declared
>>var1.callme();       // no problem, I want the defaults, I defined them
>>
>>2. Struct value in an AA.
>>
>>S[int] lookup;
>>S[100].callme() ; //Now this statement requires 7-10 lines of code.
>>
>>All this because someone was concerned with a double lookup the *first time* an object isnt found in the AA?  Semantical madness over something that isnt even a real deficiency.
>>
>>Premature optimization is a root cause of bad programming, because you end up making hard to follow code in attempts to optimize before you even know what is slow.
>>
>>This is premature optimization at the *language level*.
>>-DavidM
> 
> 
> Some of the code you mentioned has already been deprecated.
> 
> aa[key] now throws an exception if it does not exist.  Thus aa[newkey] = new
> Class is now invalid.  You must instead call aa.Add(key) 
> 

Where is this documented?

http://www.digitalmars.com/d/arrays.html#associative

I see examples of

int[char[]] b;		// associative array b of ints that are
				// indexed by an array of characters.
				// The KeyType is char[]
b["hello"] = 3;		// set value associated with key "hello" to 3

> (See changes for DMD 126: Now throws an ArrayBoundsError if accessing an
> associative array with a key that is not already in the array. Previously, the
> key would be added to the array. )
> 
> Thus your code would change to something like this
> 
> :try {
> :    foo = aa[key];
> :} catch ( Exception e ) {
> :    foo = aa.Add(key, new MyClass());
> :} finally {
> :    foo.Method();
> :}
> 
> Error handling sucks.  Personally I think AA syntax is starting to transcend
> integral types.  Since when does an integer throw an exception?  I'm all for
> making AA's a part of phobos and separating them from the language syntax.  

That is better than the previous semantics?
Compared to what? Java?  I thought D was supposed to be attracting C/C++ programmers.  Now we have checked exceptions?

> 
> 
> Also, these silly functions properties that are accessed like members are very
> confusing.   Like this:
> 
> 
> void HelloWorld(int foo) {
> writeln("Hi world, I have %d foos", foo);
> }
> 
> int foo;
> 
> foo.HelloWorld();
> 
> ^-- AAARGGGH!!!  Confusion ensues!  I'd like to see this syntax go away too.
> It's not that much effort for people who want to extend the integer type to type
> this:
> 
> HelloWorld(foo);  (which is less typing By the way, notice the lack of the extra
> period)
> 
> Which is how it belongs since it's not part of the integer type, and you can't
> find it in the class file for integer. 
> 
> 
> 

I agree you can go overboard with built in methods, however for templates they are more valuable because they can be overloaded in a class, whereas you cannot overload a specialized operator like *in*.

July 12, 2005
> void CallMethod( MyClass c, int key, MyClass[int]  lookup )
> {
>   MyClass* ptr = (key in lookup);
>   if ( ptr is null )  lookup[key] = new MyClass();
>
>   MyClass ref = lookup[key];
>   if ( ref is null )
>   {
>     ref = new MyClass();
>     lookup[key]= ref;
>   }
>   ref.Method();
> }

Once the bug that you found with & is fixed the above can be rewritten as
void CallMethod( int key, MyClass[int]  lookup )
{
   MyClass* ptr = &lookup[key];
   if ( !*ptr ) *ptr = new MyClass;
   *ptr.Method();
}


> This is starting to make C++ look clean.

Again assuming the & bug is fixed the C++ code
 map<A,B> x;
 B& ref = x[a];
can be rewritten to the D code
 B[A] x;
 B* ref = &x[a];
That is about as simple as one can expect.

> Structs on the other hand are supposed to be implicitly created. They are *values* not objects.
>
> So now we have 2 coding idioms/paradigms for structs:
> 1. Implicitly create when declared in code.
>
> struct S {
> int n = 99;
> void callme(){ n++ ; }
> }
>
> S var1, var2, var3;  // created as declared
> var1.callme();       // no problem, I want the defaults, I defined them
>
> 2. Struct value in an AA.
>
> S[int] lookup;
> S[100].callme() ; //Now this statement requires 7-10 lines of code.
>
> All this because someone was concerned with a double lookup the *first time* an object isnt found in the AA?  Semantical madness over something that isnt even a real deficiency.
>
> Premature optimization is a root cause of bad programming, because you end up making hard to follow code in attempts to optimize before you even know what is slow.
>
> This is premature optimization at the *language level*. -DavidM

Somewhere I had read that C++ chose the lookup-and-insert behavior because throwing an exception was too slow and has spotty support. In any case I know BS said that operator[] returns a reference because returning the value is too slow. So there are a couple of C++ examples where performance drove the design.


July 12, 2005
Ben Hinkle wrote:

>>void CallMethod( MyClass c, int key, MyClass[int]  lookup )
>>{
>>  MyClass* ptr = (key in lookup);
>>  if ( ptr is null )  lookup[key] = new MyClass();
>>
>>  MyClass ref = lookup[key];
>>  if ( ref is null )
>>  {
>>    ref = new MyClass();
>>    lookup[key]= ref;
>>  }
>>  ref.Method();
>>}
> 
> 
> Once the bug that you found with & is fixed the above can be rewritten as
> void CallMethod( int key, MyClass[int]  lookup )
> {
>    MyClass* ptr = &lookup[key];
>    if ( !*ptr ) *ptr = new MyClass;
>    *ptr.Method();
> }
> 
> 
> 
>>This is starting to make C++ look clean.
> 
> 
> Again assuming the & bug is fixed the C++ code
>  map<A,B> x;
>  B& ref = x[a];
> can be rewritten to the D code
>  B[A] x;
>  B* ref = &x[a];
> That is about as simple as one can expect.
> 
> 
That's beginning to look like C++.
Besides you haven't shown how that is superior to the old way.

What if I want value semantics and not call by reference?
(Which is the whole point of using a struct vs a class )
B* ref = &x[a];
B  temp = ref[0];
  // now I can use my local copy and reinsert it if needed

// versus

B temp = x[a];

The point is the change as made the code *more* complex, with zero benefits whatsoever.  If the double lookup is a non-issue because it only matters when the value is null (ie the first time).

If you look up the *in stinks* thread, Matthew starts with complaints that the in operator returned a pointer.  I actually agree with his sentiment, foreach(collections), object references(classes) and out parameters(for structs and other values) are superior to pointers.

http://www.digitalmars.com/d/archives/digitalmars/D/18450.html

Now pointers are *required*, and the code to access structures is longer(my example) or messy(your example)!!

The better alternative to *in* is a method:

B value;
if ( x.get( in a, out value ) ) { value.dostuff(); }

That is as simple as it gets, no pointers, can still use the create semantics, and if 'x' is a template alias, the x can implement .get(...) and still function with the don't create semantics.
I can't overload your example.

-DavidM
July 12, 2005
In article <db0ucs$14fp$1@digitaldaemon.com>, Ben Hinkle says...
>
>> void CallMethod( MyClass c, int key, MyClass[int]  lookup )
>> {
>>   MyClass* ptr = (key in lookup);
>>   if ( ptr is null )  lookup[key] = new MyClass();
>>
>>   MyClass ref = lookup[key];
>>   if ( ref is null )
>>   {
>>     ref = new MyClass();
>>     lookup[key]= ref;
>>   }
>>   ref.Method();
>> }
>
>Once the bug that you found with & is fixed the above can be rewritten as
>void CallMethod( int key, MyClass[int]  lookup )
>{
>   MyClass* ptr = &lookup[key];
>   if ( !*ptr ) *ptr = new MyClass;
>   *ptr.Method();
>}

This will no longer work as lookup[key] will throw an exception since of creating the key since DMD 0.126. If Add will replace existing values, or if there is a Replace method then you could still simplify it to something very close to what you have.


July 12, 2005
"David Medlock" <noone@nowhere.com> wrote in message news:db0vub$15ot$1@digitaldaemon.com...
> Ben Hinkle wrote:
>
>>>void CallMethod( MyClass c, int key, MyClass[int]  lookup )
>>>{
>>>  MyClass* ptr = (key in lookup);
>>>  if ( ptr is null )  lookup[key] = new MyClass();
>>>
>>>  MyClass ref = lookup[key];
>>>  if ( ref is null )
>>>  {
>>>    ref = new MyClass();
>>>    lookup[key]= ref;
>>>  }
>>>  ref.Method();
>>>}
>>
>>
>> Once the bug that you found with & is fixed the above can be rewritten as
>> void CallMethod( int key, MyClass[int]  lookup )
>> {
>>    MyClass* ptr = &lookup[key];
>>    if ( !*ptr ) *ptr = new MyClass;
>>    *ptr.Method();
>> }
>>
>>
>>
>>>This is starting to make C++ look clean.
>>
>>
>> Again assuming the & bug is fixed the C++ code
>>  map<A,B> x;
>>  B& ref = x[a];
>> can be rewritten to the D code
>>  B[A] x;
>>  B* ref = &x[a];
>> That is about as simple as one can expect.
>>
>>
> That's beginning to look like C++.
> Besides you haven't shown how that is superior to the old way.

I'm not sure what you mean by "old way". Can you be more specific?

> What if I want value semantics and not call by reference?
> (Which is the whole point of using a struct vs a class )
> B* ref = &x[a];
> B  temp = ref[0];
>   // now I can use my local copy and reinsert it if needed
>
> // versus
>
> B temp = x[a];
>
> The point is the change as made the code *more* complex, with zero benefits whatsoever.  If the double lookup is a non-issue because it only matters when the value is null (ie the first time).

umm - "zero benefit"? whatever...
I agree it would be nice to have an explicit method to insert-and-lookup
when that is what one wants as was discussed in the posts on the bug thread
about &.

> If you look up the *in stinks* thread, Matthew starts with complaints that the in operator returned a pointer.  I actually agree with his sentiment, foreach(collections), object references(classes) and out parameters(for structs and other values) are superior to pointers.
>
> http://www.digitalmars.com/d/archives/digitalmars/D/18450.html
>
> Now pointers are *required*, and the code to access structures is longer(my example) or messy(your example)!!
>
> The better alternative to *in* is a method:
>
> B value;
> if ( x.get( in a, out value ) ) { value.dostuff(); }
>
> That is as simple as it gets, no pointers, can still use the create semantics, and if 'x' is a template alias, the x can implement .get(...) and still function with the don't create semantics.


Various posts (I included it in my API requests) had what you call "get" by the name "contains" (though to be honest I've forgotten the details). I personally don't have anything against pointers, though, so the current 'in' is ok with me.

> I can't overload your example.

I, too, would like more overload control than what is available today. See the posts about opIndexMutable and such started by Kevin Bealer, for example.


July 12, 2005
>That's beginning to look like C++.
>Besides you haven't shown how that is superior to the old way.
>
>What if I want value semantics and not call by reference?
>(Which is the whole point of using a struct vs a class )
>B* ref = &x[a];
>B  temp = ref[0];
>   // now I can use my local copy and reinsert it if needed
>
>// versus
>
>B temp = x[a];
>
>The point is the change as made the code *more* complex, with zero benefits whatsoever.  If the double lookup is a non-issue because it only matters when the value is null (ie the first time).
>
>If you look up the *in stinks* thread, Matthew starts with complaints that the in operator returned a pointer.  I actually agree with his sentiment, foreach(collections), object references(classes) and out parameters(for structs and other values) are superior to pointers.
>
>http://www.digitalmars.com/d/archives/digitalmars/D/18450.html
>
>Now pointers are *required*, and the code to access structures is longer(my example) or messy(your example)!!
>
>The better alternative to *in* is a method:
>
>B value;
>if ( x.get( in a, out value ) ) { value.dostuff(); }
>
>That is as simple as it gets, no pointers, can still use the create
>semantics, and if 'x' is a template alias, the x can implement .get(...)
>and still function with the don't create semantics.
>I can't overload your example.
>
>-DavidM


Not to mention we have some context dependant syntax going on there with the in keyword being used for different things in different places.  IE:

int foo( in int a, inout int[int] foo ) {
MyClass *arg = a in foo;
}

See the different use?  I vote to make AA it's own class....


July 12, 2005
>>Once the bug that you found with & is fixed the above can be rewritten as
>>void CallMethod( int key, MyClass[int]  lookup )
>>{
>>   MyClass* ptr = &lookup[key];
>>   if ( !*ptr ) *ptr = new MyClass;
>>   *ptr.Method();
>>}
>
> This will no longer work as lookup[key] will throw an exception since of
> creating the key since DMD 0.126. If Add will replace existing values, or
> if
> there is a Replace method then you could still simplify it to something
> very
> close to what you have.

lookup[key] does not throw if the result is expected to be an lvalue. The problem is that there is a bug in dmd that the & operator isn't among those operators that are flagged as expecting an lvalue. See David's earlier post in this newsgroup and see my post about it in the bugs newsgroup.


July 12, 2005
Here is an example of code which was elegant, and now must be messied up because of the changes in AA(at bottom).

My choices now are to
A) switch everyhing to pointers and use &keys[...]
B) add functions which create when needed in the arrays
C) use a function to add all the keys on startup (Not feasible, as keys are updated as they are queried)

I think the code below is fairly elegant, but it will not be under the new AA.


// This code uses the glfw framework to check Keys and Mouse Movement
import glfw;

import std.stdio;

public int                   MouseX, MouseY, MouseZ, MouseDX, MouseDY, MouseDZ;


enum Mouse { Left=GLFW_MOUSE_BUTTON_LEFT, Right=GLFW_MOUSE_BUTTON_RIGHT, Middle=GLFW_MOUSE_BUTTON_MIDDLE };
enum Key { F1=GLFW_KEY_F1, F2=GLFW_KEY_F2, F3=GLFW_KEY_F3, F4=GLFW_KEY_F4 };


private:
struct keystate
{
  bool    down      = false;
  bool    changed   = false;
  bool    updated   = false;

  bool    released() { return (!down) && changed; }
  bool    pressed()  { return down && changed; }

  protected void update( bool isdown )
  {
    changed = ( down != isdown );
    down = isdown;
    updated = true;
  }
}


keystate[ int ]         keys;
keystate[ int ]         mbutton;


// mouse buttons are updated each Update, so just return the state
keystate    getmouse( int btn ){ return mbutton[ btn ]; }

keystate    getkey( int keycode )
{
  keystate st = keys[keycode];
  if ( !st.updated ) {
    st.update( glfwGetKey( keycode )==GLFW_PRESS );
    keys[keycode] = st;
  }
  return st;
}


public:

void ClearInput()
{
  foreach( int code; keys.keys ) delete keys[code];
  foreach( int code; mbutton.keys ) delete mbutton[code];
  MouseDX = MouseDY = MouseDZ = 0;
}

void CheckInput()
{
  foreach( int kcode; keys.keys )  (kcode in keys).updated = false;

  glfwPollEvents();

  int x, y, z;

  mbutton[Mouse.Left].update( glfwGetMouseButton( Mouse.Left )==GLFW_PRESS );
  mbutton[Mouse.Middle].update( glfwGetMouseButton( Mouse.Middle )==GLFW_PRESS );
  mbutton[Mouse.Right].update( glfwGetMouseButton( Mouse.Right )==GLFW_PRESS );

  z = glfwGetMouseWheel();
  glfwGetMousePos( &x, &y );
  MouseDX = x - MouseX;
  MouseDY = y - MouseY;
  MouseDZ = z - MouseZ;
  MouseX = x;
  MouseY = y;
  MouseZ = z;
}


// key was just pressed
bool    KeyPressed( int code ) { return getkey( code ).pressed; }

// key was just released
bool    KeyReleased( int code ) { return getkey( code ).released;}

// key is currently down
bool    KeyDown( int code ) { return getkey( code ).down ; }

// key is currently up
bool    KeyUp( int code )   { return getkey( code ).down==false; }


// mouse has moved since last update
bool    MouseMove() { return (MouseDX!=0) || (MouseDY!=0) || (MouseDZ!=0 ); }

bool    MouseDown(int btn) { return getmouse( btn ).down; }
bool    MouseUp( int btn ) { return false==getmouse( btn ).down; }
bool    MouseReleased( int btn ) { return getmouse(btn).released; }
bool    MousePressed( int btn )  { return getmouse(btn).pressed; }




« First   ‹ Prev
1 2 3 4