www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - AA question

reply bobef <bobef_member pathlink.com> writes:
When I try to read (AA is right of the = ) key that does not exists from AA it
gets created. Is this normal?
I mean something like this:

foo[bar] aa;
for bb=aa[bar_that_doesnt_exists_in_the_aa];

And then bb is not null but new foo instead. And is even not new foo because if
it is class and I read from it program crashes...
Apr 08 2005
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
You are correct.  If you want to check if a given key exists, then use 
the expression:
	<key> in <variable>
which returns a pointer.  If the key does NOT exist, then it returns 
null and the key is NOT added to the array. But if the key exists, then 
it returns a pointer to the value.

bobef wrote:
 When I try to read (AA is right of the = ) key that does not exists from AA it
 gets created. Is this normal?
 I mean something like this:
 
 foo[bar] aa;
 for bb=aa[bar_that_doesnt_exists_in_the_aa];
 
 And then bb is not null but new foo instead. And is even not new foo because if
 it is class and I read from it program crashes...

Apr 08 2005
parent reply bobef <bobef_member pathlink.com> writes:
Yes I know that. But I think new value should be added only on assigment...
Am I wrong? And if I do why?

In article <d36jna$3cs$1 digitaldaemon.com>, Russ Lewis says...
You are correct.  If you want to check if a given key exists, then use 
the expression:
	<key> in <variable>
which returns a pointer.  If the key does NOT exist, then it returns 
null and the key is NOT added to the array. But if the key exists, then 
it returns a pointer to the value.

bobef wrote:
 When I try to read (AA is right of the = ) key that does not exists from AA it
 gets created. Is this normal?
 I mean something like this:
 
 foo[bar] aa;
 for bb=aa[bar_that_doesnt_exists_in_the_aa];
 
 And then bb is not null but new foo instead. And is even not new foo because if
 it is class and I read from it program crashes...


Apr 08 2005
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
This has been argued extensively before; some people think that it is a 
good idea; others do not.  Frankly, I'm not too up-to-speed on the exact 
reasoning, but you should be able to search the newsgroup for info.

Anybody have a link, or a message name?

bobef wrote:
 Yes I know that. But I think new value should be added only on assigment...
 Am I wrong? And if I do why?
 
 In article <d36jna$3cs$1 digitaldaemon.com>, Russ Lewis says...
 
You are correct.  If you want to check if a given key exists, then use 
the expression:
	<key> in <variable>
which returns a pointer.  If the key does NOT exist, then it returns 
null and the key is NOT added to the array. But if the key exists, then 
it returns a pointer to the value.

bobef wrote:

When I try to read (AA is right of the = ) key that does not exists from AA it
gets created. Is this normal?
I mean something like this:

foo[bar] aa;
for bb=aa[bar_that_doesnt_exists_in_the_aa];

And then bb is not null but new foo instead. And is even not new foo because if
it is class and I read from it program crashes...



Apr 08 2005
parent reply Georg Wrede <georg.wrede nospam.org> writes:
Russ Lewis wrote:
 This has been argued extensively before; some people think that it is a 
 good idea; others do not.  Frankly, I'm not too up-to-speed on the exact 
 reasoning, but you should be able to search the newsgroup for info.
 
 Anybody have a link, or a message name?

Let's just say, I'd like to hear the motivations for the current behavior (again)!
 bobef wrote:
 
 Yes I know that. But I think new value should be added only on 
 assigment...
 Am I wrong? And if I do why?

 In article <d36jna$3cs$1 digitaldaemon.com>, Russ Lewis says...

 You are correct.  If you want to check if a given key exists, then 
 use the expression:
     <key> in <variable>
 which returns a pointer.  If the key does NOT exist, then it returns 
 null and the key is NOT added to the array. But if the key exists, 
 then it returns a pointer to the value.

 bobef wrote:

 When I try to read (AA is right of the = ) key that does not exists 
 from AA it
 gets created. Is this normal?
 I mean something like this:

 foo[bar] aa;
 for bb=aa[bar_that_doesnt_exists_in_the_aa];

 And then bb is not null but new foo instead. And is even not new foo 
 because if
 it is class and I read from it program crashes...




Apr 08 2005
parent reply "Charlie" <charles jwavro.com> writes:
 Let's just say, I'd like to hear the motivations for the current
 behavior (again)!

I'd like to hear these too , is any one actually _for_ this ? Charlie "Georg Wrede" <georg.wrede nospam.org> wrote in message news:4257003D.3030706 nospam.org...
 Russ Lewis wrote:
 This has been argued extensively before; some people think that it is a
 good idea; others do not.  Frankly, I'm not too up-to-speed on the exact
 reasoning, but you should be able to search the newsgroup for info.

 Anybody have a link, or a message name?

Let's just say, I'd like to hear the motivations for the current behavior (again)!
 bobef wrote:

 Yes I know that. But I think new value should be added only on
 assigment...
 Am I wrong? And if I do why?

 In article <d36jna$3cs$1 digitaldaemon.com>, Russ Lewis says...

 You are correct.  If you want to check if a given key exists, then
 use the expression:
     <key> in <variable>
 which returns a pointer.  If the key does NOT exist, then it returns
 null and the key is NOT added to the array. But if the key exists,
 then it returns a pointer to the value.

 bobef wrote:

 When I try to read (AA is right of the = ) key that does not exists
 from AA it
 gets created. Is this normal?
 I mean something like this:

 foo[bar] aa;
 for bb=aa[bar_that_doesnt_exists_in_the_aa];

 And then bb is not null but new foo instead. And is even not new foo
 because if
 it is class and I read from it program crashes...





Apr 08 2005
parent reply "Uwe Salomon" <post uwesalomon.de> writes:
 Let's just say, I'd like to hear the motivations for the current
 behavior (again)!

I'd like to hear these too , is any one actually _for_ this ?

Yes. I am. This is like it's done in Qt, and I think it is quite useful. There are two operators for AAs: [] - insert (+retrieve) value in - test (+retrieve) value As you can see, there already is an operator for looking up a value (it returns a pointer to the value, i think, or null if it isn't in the AA). So you can change it if it is found, without looking it up again. If you would change the behaviour of [], there would be no operator for "give me the value to key X, or the default if there isn't an X in the map". It is very important to have an operator for every of these use-cases, because all workarounds are much slower and would thus render the AA useless for programmers needing the speed. Ciao uwe
Apr 08 2005
next sibling parent reply Ben Hinkle <Ben_member pathlink.com> writes:
In article <opsoxq1ipy6yjbe6 sandmann.maerchenwald.net>, Uwe Salomon says...
 Let's just say, I'd like to hear the motivations for the current
 behavior (again)!

I'd like to hear these too , is any one actually _for_ this ?

Yes. I am. This is like it's done in Qt, and I think it is quite useful. There are two operators for AAs: [] - insert (+retrieve) value in - test (+retrieve) value As you can see, there already is an operator for looking up a value (it returns a pointer to the value, i think, or null if it isn't in the AA). So you can change it if it is found, without looking it up again. If you would change the behaviour of [], there would be no operator for "give me the value to key X, or the default if there isn't an X in the map". It is very important to have an operator for every of these use-cases, because all workarounds are much slower and would thus render the AA useless for programmers needing the speed.

I think most people here would agree. The question is if [] and "in" are those operators. I had always assumed C++ inserts on [] because it returns a reference for either lvalue or rvalue contexts. In D the compiler detects the most common rvalue context (opIndexAssign) and we can have [] insert in that case and not insert otherwise. I had made a few posts about why inserting on lookup can be bad: it prevents properties from returning an AA (properties can return dynamic arrays just fine) and it makes concurrent AAs use a different API since changing the container on lookup is a big no-no for multi-threaded apps.
Apr 09 2005
next sibling parent Ben Hinkle <Ben_member pathlink.com> writes:
In D the compiler detects the most common
rvalue context (opIndexAssign) 

oops- I meant lvalue. :-P To make this post more interesting, though, let me toss in a URL: http://www.planetfall.pwp.blueyonder.co.uk/notcpp.html the section about lvalues and operator overloading.
Apr 09 2005
prev sibling parent reply "Uwe Salomon" <post uwesalomon.de> writes:
 I think most people here would agree. The question is if [] and "in" are  
 those operators.

One could discuss about it, yes. But i want to see DMD 1.0, thus i try not to request any new features or changes in the language. This solutions works, and it's not a bad solution. By the way, is there an opLookup() or something else to overload the "in" operator?
 In D the compiler detects the most common
 rvalue context (opIndexAssign) and we can have [] insert in that case  
 and not insert otherwise.

You mean if you write your own associative container, you would write opIndex() to *not* insert the lookup-value? Hm, i think it's better to provide a member function doing that (if there is no opLookup), to make the use consistent.
 it prevents
 properties from returning an AA (properties can return dynamic arrays  
 just fine)

I don't understand that. You mean a function like that is not possible: struct SomeStruct { int[char[]] property() { return something; } } void func() { SomeStruct struc; printf("%i\n", struc.property["name"]); }
 and it makes concurrent AAs use a different API since changing the  
 container on lookup is a big no-no for multi-threaded apps.

Well, i would rather change the api to reflect that. Don't provide opIndex[] for concurrent AAs. By the way, i like the implementation of your MinTL very much. I have written a "vector" container which provides full Qt, STL and D API (except the buggy opCmp/opEqual, they are commented out), and handles the memory management for himself. If someone is interested: http://www.uwesalomon.de/code/vector.d (doc is doxygen, but i didn't generate it. General explanation beginning in line 248. But it's like Qt.) http://www.uwesalomon.de/code/global.d (needed as well.) I will write some other containers as i need them, at least a linked list and a map (perhaps a red/tree-map as well to learn the algorithm). Is there a need for the full set of Qt containers (Vector, List, LinkedList, Map, HashMap; perhaps VarLengthArray)? That would make me implement them all... Ciao uwe
Apr 09 2005
next sibling parent reply Thomas Kuehne <thomas-dloop kuehne.thisisspam.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Uwe Salomon schrieb am Sat, 09 Apr 2005 18:26:12 +0200:
 By the way, i like the implementation of your MinTL very much. I have  
 written a "vector" container which provides full Qt, STL and D API (except  
 the buggy opCmp/opEqual, they are commented out), and handles the memory  
 management for himself. If someone is interested:

 http://www.uwesalomon.de/code/vector.d (doc is doxygen, but i didn't  
 generate it. General explanation beginning in line 248. But it's like Qt.)
 http://www.uwesalomon.de/code/global.d (needed as well.)

"Implementirung. Operazionen. Konstrukzion/destrukzion." I've never seen Ultradeutsh before :P Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFCWA2I3w+/yD4P9tIRAoDtAJ4mMVXLKaC6Tbf7Fb9+3tHKoMk7RwCghgP0 lIJozj8tX0OgboOYqbd6j+o= =h0Uz -----END PGP SIGNATURE-----
Apr 09 2005
parent "Uwe Salomon" <post uwesalomon.de> writes:
 "Implementirung. Operazionen. Konstrukzion/destrukzion."

 I've never seen Ultradeutsh before :P

Ah. You know Ultradeutsh? Actually, i use wunschdeutsch in the code comments... But perhaps this is a bit OT here ;o) Ciao uwe
Apr 09 2005
prev sibling parent reply Ben Hinkle <Ben_member pathlink.com> writes:
In article <opsoy3xye46yjbe6 sandmann.maerchenwald.net>, Uwe Salomon says...
 I think most people here would agree. The question is if [] and "in" are  
 those operators.

One could discuss about it, yes. But i want to see DMD 1.0, thus i try not to request any new features or changes in the language. This solutions works, and it's not a bad solution.

It's not a total disaster, true, but if there is a better solution we should find it before 1.0 otherwise the bar goes way up for changing behavior IMO.
By the way, is there an opLookup() or  
something else to overload the "in" operator?

no. In my code I've been using opIn as a placeholder.
 In D the compiler detects the most common
 rvalue context (opIndexAssign) and we can have [] insert in that case  
 and not insert otherwise.

You mean if you write your own associative container, you would write opIndex() to *not* insert the lookup-value? Hm, i think it's better to provide a member function doing that (if there is no opLookup), to make the use consistent.

I'm saying we don't have to follow the C++ model due to language behaviors. The D language can allow us to change [] in an rvalue context to not insert. In C++ it must insert - there's basically no choice.
 it prevents
 properties from returning an AA (properties can return dynamic arrays  
 just fine)

I don't understand that. You mean a function like that is not possible: struct SomeStruct { int[char[]] property() { return something; } } void func() { SomeStruct struc; printf("%i\n", struc.property["name"]); }

correct. If property returned a dynamic array the code struc.property["name"] would be ok. With AAs it is not.
 and it makes concurrent AAs use a different API since changing the  
 container on lookup is a big no-no for multi-threaded apps.

Well, i would rather change the api to reflect that. Don't provide opIndex[] for concurrent AAs.

eek. That's a very convenient syntax that I think users would be sad to see unsupported.
By the way, i like the implementation of your MinTL very much. I have  
written a "vector" container which provides full Qt, STL and D API (except  
the buggy opCmp/opEqual, they are commented out), and handles the memory  
management for himself. If someone is interested:

http://www.uwesalomon.de/code/vector.d (doc is doxygen, but i didn't  
generate it. General explanation beginning in line 248. But it's like Qt.)
http://www.uwesalomon.de/code/global.d (needed as well.)

I will write some other containers as i need them, at least a linked list  
and a map (perhaps a red/tree-map as well to learn the algorithm). Is  
there a need for the full set of Qt containers (Vector, List, LinkedList,  
Map, HashMap; perhaps VarLengthArray)? That would make me implement them  
all...

Cool! I hadn't seen this code before. Maybe I just missed it but I would post an announcement to the D.announce and/or the D.dtl groups.
Apr 09 2005
parent "Uwe Salomon" <post uwesalomon.de> writes:
 http://www.uwesalomon.de/code/vector.d (doc is doxygen, but i didn't
 generate it. General explanation beginning in line 248. But it's like  
 Qt.)
 http://www.uwesalomon.de/code/global.d (needed as well.)

Cool! I hadn't seen this code before. Maybe I just missed it but I would post an announcement to the D.announce and/or the D.dtl groups.

You couldn't see it before. I uploaded it today and didn't post anything anywhere except this message. But after finishing the list i will post in D.announce. Anywhere else i should write something? In the Wiki? (You see, i'm not very experienced with "open source development" :o). Ah, if you like you can incorporate the vector in your library (if that's possible and feasible). I did some benchmarking with appending a lot of elements one after the other, and it is quite a bit faster than temporarily increasing the length to avoid reallocations. Ciao uwe
Apr 09 2005
prev sibling next sibling parent reply novice2 <novice2_member pathlink.com> writes:
In article <opsoxq1ipy6yjbe6 sandmann.maerchenwald.net>, Uwe Salomon says...

If you  
would change the behaviour of [], there would be no operator for "give me  
the value to key X, or the default if there isn't an X in the map".

No! "Giving default" if X not exists and "inserting if not exists" are very different. Let's reading aa[not_exist_key] give default, but not write any data to AA. This is side effect. After this "in" operator useless. Information about "was element exists in AA" lost.
Apr 10 2005
parent reply "Uwe Salomon" <post uwesalomon.de> writes:
 If you
 would change the behaviour of [], there would be no operator for "give  
 me
 the value to key X, or the default if there isn't an X in the map".

No! "Giving default" if X not exists and "inserting if not exists" are very different. Let's reading aa[not_exist_key] give default, but not write any data to AA. This is side effect. After this "in" operator useless. Information about "was element exists in AA" lost.

Jep, sorry. I meant "lookup X and insert default if X doesn't exist". Thus "in" only does a lookup, and "[]" inserts if necessary. Ciao uwe
Apr 10 2005
next sibling parent reply Ben Hinkle <Ben_member pathlink.com> writes:
In article <opso0b4nv26yjbe6 sandmann.maerchenwald.net>, Uwe Salomon says...
 If you
 would change the behaviour of [], there would be no operator for "give  
 me
 the value to key X, or the default if there isn't an X in the map".

No! "Giving default" if X not exists and "inserting if not exists" are very different. Let's reading aa[not_exist_key] give default, but not write any data to AA. This is side effect. After this "in" operator useless. Information about "was element exists in AA" lost.

Jep, sorry. I meant "lookup X and insert default if X doesn't exist". Thus "in" only does a lookup, and "[]" inserts if necessary.

My preferred AA api, as posted a while ago to Matthew's "in stinks" thread: bit opIn(Key key) // possibly return value* instead Value opIndex(Key key) // throws on missing key void opIndexAssign(Value value, Key key) // insert bit contains(Key key, out Value value) void remove(Key key) // ignores missing key Value* insert(Key key) // lookup and insert if not present .. rest as before except "delete" is removed... The functions with name opFoo are for the operators in, [] and []= -Ben ps - it would be nice to hear boo from Walter what chances any changes to AAs have of happening. I have the feeling the chances are around 10% or less.
Apr 10 2005
parent reply "Matthew" <admin stlsoft.dot.dot.dot.dot.org> writes:
"Ben Hinkle" <Ben_member pathlink.com> wrote in message 
news:d3bg60$2n7q$1 digitaldaemon.com...
 In article <opso0b4nv26yjbe6 sandmann.maerchenwald.net>, Uwe 
 Salomon says...
 If you
 would change the behaviour of [], there would be no operator 
 for "give
 me
 the value to key X, or the default if there isn't an X in the 
 map".

No! "Giving default" if X not exists and "inserting if not exists" are very different. Let's reading aa[not_exist_key] give default, but not write any data to AA. This is side effect. After this "in" operator useless. Information about "was element exists in AA" lost.

Jep, sorry. I meant "lookup X and insert default if X doesn't exist". Thus "in" only does a lookup, and "[]" inserts if necessary.

My preferred AA api, as posted a while ago to Matthew's "in stinks" thread: bit opIn(Key key) // possibly return value* instead

Agree, apart from Value* part
 Value opIndex(Key key) // throws on missing key

Agree
 void opIndexAssign(Value value, Key key) // insert

Agree
 bit contains(Key key, out Value value)

Agree. (I think it was me that suggested this one, so am bound to agree <g>)
 void remove(Key key) // ignores missing key

Don't agree with ignoring missing thing, although not 100% about it
 Value* insert(Key key) // lookup and insert if not present

Don't get this? What get's inserted? Surely it needs a second param, as in <whatever> insert(Key key, Value value): ?? Now, as to <whatever> is, that depends on how you respond to the above question. My suspicion is that you've used Value* so that some can write to it, which I think sucks, I'm afraid. I really don't think D should be flirting with pointers in such cases. Also, what's wrong with the (Key, Value) syntax?
 .. rest as before except "delete" is removed...

Agree
 -Ben

 ps - it would be nice to hear boo from Walter what chances any 
 changes to AAs
 have of happening.

Seconded.
 I have the feeling the chances are around 10% or less.

Optimist!
Apr 13 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
 My preferred AA api, as posted a while ago to Matthew's "in stinks" 
 thread:
 bit opIn(Key key) // possibly return value* instead

Agree, apart from Value* part

my preference, too. I haven't thought too hard about use cases for the value* form.
 Value opIndex(Key key) // throws on missing key

Agree
 void opIndexAssign(Value value, Key key) // insert

Agree
 bit contains(Key key, out Value value)

Agree. (I think it was me that suggested this one, so am bound to agree <g>)

I should have been more careful in attributing the different ideas since my saying "my preferred API" implicitly means I came up with the API, which isn't right.
 void remove(Key key) // ignores missing key

Don't agree with ignoring missing thing, although not 100% about it

I figured removing something that doesn't exist is a no-op. The post-condition that the AA doesn't contain the key is true.
 Value* insert(Key key) // lookup and insert if not present

Don't get this? What get's inserted? Surely it needs a second param, as in <whatever> insert(Key key, Value value): ?? Now, as to <whatever> is, that depends on how you respond to the above question. My suspicion is that you've used Value* so that some can write to it, which I think sucks, I'm afraid. I really don't think D should be flirting with pointers in such cases. Also, what's wrong with the (Key, Value) syntax?

I have a feeling Walter likes the current behavior because of the example in the Array section with the magic line count[word]++; And so I wanted to supply something similar and the best way I could see was "insert": (*count.insert(word))++ A better name would help. I thought of calling it insertKey but then I shortened it to just insert, perhaps by mistake. Having insert take a key and value makes it the effectively the same as opIndexAssign, which seems redundant. If you scroll up you'll see I put the comment "// insert" next to opIndexAssign so arguably this "insert" name needs to change.
Apr 14 2005
parent reply "Matthew" <admin.hat stlsoft.dot.org> writes:
 bit contains(Key key, out Value value)

Agree. (I think it was me that suggested this one, so am bound to agree <g>)

I should have been more careful in attributing the different ideas since my saying "my preferred API" implicitly means I came up with the API, which isn't right.

It was no problem. Just yanking ya chain. ;)
 void remove(Key key) // ignores missing key

Don't agree with ignoring missing thing, although not 100% about it

I figured removing something that doesn't exist is a no-op. The post-condition that the AA doesn't contain the key is true.

I'm still feeling a bit unsure, but I always err on the side of strictness. If people aren't too tired by now, I'd be interested in hearing thoughts on the matter.
 Value* insert(Key key) // lookup and insert if not present

Don't get this? What get's inserted? Surely it needs a second param, as in <whatever> insert(Key key, Value value): ?? Now, as to <whatever> is, that depends on how you respond to the above question. My suspicion is that you've used Value* so that some can write to it, which I think sucks, I'm afraid. I really don't think D should be flirting with pointers in such cases. Also, what's wrong with the (Key, Value) syntax?

I have a feeling Walter likes the current behavior because of the example in the Array section with the magic line count[word]++; And so I wanted to supply something similar and the best way I could see was "insert": (*count.insert(word))++ A better name would help. I thought of calling it insertKey but then I shortened it to just insert, perhaps by mistake. Having insert take a key and value makes it the effectively the same as opIndexAssign, which seems redundant. If you scroll up you'll see I put the comment "// insert" next to opIndexAssign so arguably this "insert" name needs to change.

Ok. Maybe we need to have code samples / use cases for all the proposed syntax? (I'm such a dumb ass, as I really need to see code in order to think about it <g>) I just have a really bad feeling about pointers being anywhere in the syntax for D for things that do not directly pertain to talking to C APIs. Otherwise, why don't we just drop the ambiguities that come with references, go with pointers for objects, and clear up the identity/equality issue? <g>
Apr 14 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
 void remove(Key key) // ignores missing key

Don't agree with ignoring missing thing, although not 100% about it

I figured removing something that doesn't exist is a no-op. The post-condition that the AA doesn't contain the key is true.

I'm still feeling a bit unsure, but I always err on the side of strictness. If people aren't too tired by now, I'd be interested in hearing thoughts on the matter.

To elaborate on my first reply, to me removing a key that isn't in the array is similar to closing a stream that is already closed. I see both situations as no-ops since the requested end-state is already satisfied. The basic principle is "no harm no foul". I'd be interesting in more opinions, too, though.
 Value* insert(Key key) // lookup and insert if not present

Don't get this? What get's inserted? Surely it needs a second param, as in <whatever> insert(Key key, Value value):


Ok. Maybe we need to have code samples / use cases for all the proposed syntax? (I'm such a dumb ass, as I really need to see code in order to think about it <g>)

Here's the API with more details/examples. The array is variable "x", the key "key" and the value "val". function: bit opIn(Key key) example: if (key in x) {...} Returns true iff key is in x. function: Value opIndex(Key key) (note this is internal to the compiler impl and isn't user visible) example: val = x[key]; Lookup and return the value for key. Throws an exeption if key is not in x. Result is not an lvalue. function: void opIndexAssign(Value val, Key key) (compiler internal impl) example: x[key] = val; Associates val with key. Inserts if key is not in x and otherwise overwrites previous value. This is the only syntax where x[key] can be used as an lvalue. function: bit contains(Key key, out Value val) example: if (x.contains(key,val)) {...} Returns true iff key is in x. If key is in x assigns the associated value to val. function: void remove(Key key) example: x.remove(key); Removes key from x if key is in x. function: Value* insert(Key key) examples: (*x.insert(key)) += 10; foo(*x.insert(key)) // where foo is void foo(out Value val); If key is in x returns a pointer to the associated value and otherwise insert key with the default value and return a pointer to it. Note that x[key] = value is equivalent to *x.insert(key) = value; I'm tempted to add some more like Value replace(Key key, Value value) Value* query(Key key) // return null if key not in x but I'll hold off on those. They would be nice to have IMO. The reason for adding more sophisticated functions for AAs than dynamic arrays is that writing multiple lookups for AA is significantly more expensive for performance than dynamic arrays.
 I just have a really bad feeling about pointers being anywhere in the 
 syntax for D for things that do not directly pertain to talking to C APIs. 
 Otherwise, why don't we just drop the ambiguities that come with 
 references, go with pointers for objects, and clear up the 
 identity/equality issue? <g>

Exposing the pointer would only happen when you can't use the x[key]=value syntax. The "advanced maneuver" of inserting, initializing and returning an lvalue needs to be possible and that's where the pointers/references come in. If D had references or out-references or whatever then I agree it probably would be nice to remove the pointer stuff. Or if D had a different syntax than x[key] for indicating it should return an lvalue that would work, too.
Apr 14 2005
parent "Matthew" <admin stlsoft.dot.dot.dot.dot.org> writes:
"Ben Hinkle" <bhinkle mathworks.com> wrote in message 
news:d3lulk$11pd$1 digitaldaemon.com...
 void remove(Key key) // ignores missing key

Don't agree with ignoring missing thing, although not 100% about it

I figured removing something that doesn't exist is a no-op. The post-condition that the AA doesn't contain the key is true.

I'm still feeling a bit unsure, but I always err on the side of strictness. If people aren't too tired by now, I'd be interested in hearing thoughts on the matter.

To elaborate on my first reply, to me removing a key that isn't in the array is similar to closing a stream that is already closed. I see both situations as no-ops since the requested end-state is already satisfied. The basic principle is "no harm no foul". I'd be interesting in more opinions, too, though.

Impishly mixing threads of contention(/contentiousness), I'd say that if it's part of the contract of remove(), then there's no problem. ;)
 Value* insert(Key key) // lookup and insert if not present

Don't get this? What get's inserted? Surely it needs a second param, as in <whatever> insert(Key key, Value value):


Ok. Maybe we need to have code samples / use cases for all the proposed syntax? (I'm such a dumb ass, as I really need to see code in order to think about it <g>)

Here's the API with more details/examples. The array is variable "x", the key "key" and the value "val". function: bit opIn(Key key) example: if (key in x) {...} Returns true iff key is in x.

Totally agree
 function:
  Value opIndex(Key key) (note this is internal to the compiler 
 impl and isn't user visible)
 example:
  val = x[key];
  Lookup and return the value for key. Throws an exeption if key is 
 not in x. Result is not an lvalue.

Totally agree.
 function:
  void opIndexAssign(Value val, Key key) (compiler internal impl)
 example:
  x[key] = val;
  Associates val with key. Inserts if key is not in x and otherwise 
 overwrites previous value. This is the only syntax where x[key] 
 can be used as an lvalue.

Totally agree
 function:
  bit contains(Key key, out Value val)
 example:
  if (x.contains(key,val)) {...}
  Returns true iff key is in x. If key is in x assigns the 
 associated value to val.

Agree, except I think there should also be an overload that doesn't take the out value, i.e. is just a method-form equivalent to "in"
 function:
  void remove(Key key)
 example:
  x.remove(key);
  Removes key from x if key is in x.

Totally agree
 function:
  Value* insert(Key key)
 examples:
  (*x.insert(key)) += 10;
  foo(*x.insert(key)) // where foo is void foo(out Value val);
  If key is in x returns a pointer to the associated value and 
 otherwise insert key with the default value and return a pointer 
 to it. Note that x[key] = value is equivalent to *x.insert(key) = 
 value;

Don't like it, I'm afraid. Been reading up on some more 'advanced' Python recently, and the use of * there seems exceedingly foreign and hacky. This gives me the very same impression. I respectfully submit that both your examples syntactically suck. I see where you're coming from, though, which I'd not done earlier in the thread. What you're looking for is how to support a somewhat-analoguous behaviour for other operations as exists for []=. In C++ we'd return a reference from insert() and it'd be simple. Do I rightly recall recent calls for returning lvalues from functions? If so, that'd be the right approach. If not, I think your *-hack is kind of sticking plaster on a deeper inadequacy in D. I feel/hope that there's a solution to the wider issue, but nothing's coming to mind at the moment. I suggest this one is marked for further discussion.
 I'm tempted to add some more like
  Value replace(Key key, Value value)
  Value* query(Key key) // return null if key not in x
 but I'll hold off on those. They would be nice to have IMO. The 
 reason for adding more sophisticated functions for AAs than 
 dynamic arrays is that writing multiple lookups for AA is 
 significantly more expensive for performance than dynamic arrays.

 I just have a really bad feeling about pointers being anywhere in 
 the syntax for D for things that do not directly pertain to 
 talking to C APIs. Otherwise, why don't we just drop the 
 ambiguities that come with references, go with pointers for 
 objects, and clear up the identity/equality issue? <g>

Exposing the pointer would only happen when you can't use the x[key]=value syntax. The "advanced maneuver" of inserting, initializing and returning an lvalue needs to be possible and that's where the pointers/references come in. If D had references or out-references or whatever then I agree it probably would be nice to remove the pointer stuff. Or if D had a different syntax than x[key] for indicating it should return an lvalue that would work, too.

Both interesting. I suspect Walter might have one of his inspirations on this one, if we can sufficiently convince him that the pointer stuff is un appealing.
Apr 15 2005
prev sibling parent reply =?UTF-8?B?QW5kZXJzIEYgQmrDtnJrbHVuZA==?= <afb algonet.se> writes:
Uwe Salomon wrote:

 If you would change the behaviour of [], there would be no
 operator for "give  me the value to key X, or the default if
 there isn't an X in the map".

No! "Giving default" if X not exists and "inserting if not exists" are very different. Let's reading aa[not_exist_key] give default, but not write any data to AA. This is side effect. After this "in" operator useless. Information about "was element exists in AA" lost.

Jep, sorry. I meant "lookup X and insert default if X doesn't exist". Thus "in" only does a lookup, and "[]" inserts if necessary.

Do you have any real-life examples of the "give me the value of key X or create and insert a new zero entry for key X and return it to me" ? I know that C++/STL "map" does it that way, just wondered how it would be used in actual code (as I happen to think that it's an ugly bug...) Inserting for lvalues is OK and kinda neat, but inserting for rvalues ? (it would most definitely have to be documented in <blink> tags, or so) And like has been mentioned over and over, it's not even a .init/new - but just a dummy entry full of zeroes ? (just allocated with calloc...) --anders
Apr 10 2005
next sibling parent reply Ben Hinkle <Ben_member pathlink.com> writes:
Do you have any real-life examples of the "give me the value of key X
or create and insert a new zero entry for key X and return it to me" ?

The word-count example at the bottom of the Array doc has something like count[word]++; which only works because the counts are ints and start at 0. Note also no user-defined type could support syntax like that without changing the language somehow (eg by adding references).
Apr 10 2005
parent reply =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Ben Hinkle wrote:

 The word-count example at the bottom of the Array doc has something like
 count[word]++;
 which only works because the counts are ints and start at 0.

I think it would work the same if rvalue defaulted to zero, and lvalue inserted the new value ? Assuming that ++ expands to something similar to: "count[word] = count[word] + 1;" ? --anders
Apr 10 2005
parent reply "Uwe Salomon" <post uwesalomon.de> writes:
 I think it would work the same if rvalue defaulted to zero,
 and lvalue inserted the new value ? Assuming that ++ expands
 to something similar to: "count[word] = count[word] + 1;" ?

But ++ (and += and friends) is guaranteed to evaluate the operand only once... Ciao uwe
Apr 10 2005
parent =?UTF-8?B?QW5kZXJzIEYgQmrDtnJrbHVuZA==?= <afb algonet.se> writes:
Uwe Salomon wrote:

 I think it would work the same if rvalue defaulted to zero,
 and lvalue inserted the new value ? Assuming that ++ expands
 to something similar to: "count[word] = count[word] + 1;" ?

But ++ (and += and friends) is guaranteed to evaluate the operand only once...

Maybe it really is broken by design, then. That's too bad... --anders
Apr 10 2005
prev sibling parent "Uwe Salomon" <post uwesalomon.de> writes:
 Inserting for lvalues is OK and kinda neat, but inserting for rvalues ?
 (it would most definitely have to be documented in <blink> tags, or so)

 And like has been mentioned over and over, it's not even a .init/new -
 but just a dummy entry full of zeroes ? (just allocated with calloc...)

Hm. You are right. Perhaps this should be changed. Everything i needed until now was a function like this: T lookup(Key key, T defaultValue) which returns the value for key or defaultValue if key isn't in the map. No fancy inserting... Ciao uwe
Apr 10 2005
prev sibling parent bobef <bobef_member pathlink.com> writes:
It think this behavior is not only *strange* but stupid, I believe, and it
differs from all other [] behavior. If it was normal, [] would be test
(+retrieve) value
and []= would be insert. It is clear and straight forward... And if it stays
this way it should be mentioned with BIG asterix in the docs because it is
different from all other D and noone would think it will behave like that...

In article <opsoxq1ipy6yjbe6 sandmann.maerchenwald.net>, Uwe Salomon says...
 Let's just say, I'd like to hear the motivations for the current
 behavior (again)!

I'd like to hear these too , is any one actually _for_ this ?

Yes. I am. This is like it's done in Qt, and I think it is quite useful. There are two operators for AAs: [] - insert (+retrieve) value in - test (+retrieve) value As you can see, there already is an operator for looking up a value (it returns a pointer to the value, i think, or null if it isn't in the AA). So you can change it if it is found, without looking it up again. If you would change the behaviour of [], there would be no operator for "give me the value to key X, or the default if there isn't an X in the map". It is very important to have an operator for every of these use-cases, because all workarounds are much slower and would thus render the AA useless for programmers needing the speed. Ciao uwe

Apr 10 2005